this repo has no description
1use super::stream::{copy_stream, guard_file_output, open_input, prepare_output};
2use crate::{
3 progress::ProgressArgs,
4 utils::{
5 CmprssInput, CmprssOutput, CommonArgs, CompressionLevelValidator, Compressor, LevelArgs,
6 Result,
7 },
8};
9use bzip2::Compression;
10use bzip2::write::{BzDecoder, BzEncoder};
11use clap::Args;
12
13/// BZip2-specific compression validator (1-9 range)
14#[derive(Debug, Clone, Copy)]
15pub struct Bzip2CompressionValidator;
16
17impl CompressionLevelValidator for Bzip2CompressionValidator {
18 fn min_level(&self) -> i32 {
19 1
20 }
21 fn max_level(&self) -> i32 {
22 9
23 }
24 fn default_level(&self) -> i32 {
25 9
26 }
27
28 fn name_to_level(&self, name: &str) -> Option<i32> {
29 match name.to_lowercase().as_str() {
30 "fast" => Some(1),
31 "best" => Some(9),
32 _ => None,
33 }
34 }
35}
36
37#[derive(Args, Debug)]
38pub struct Bzip2Args {
39 #[clap(flatten)]
40 pub common_args: CommonArgs,
41
42 #[clap(flatten)]
43 pub progress_args: ProgressArgs,
44
45 #[clap(flatten)]
46 pub level_args: LevelArgs,
47}
48
49#[derive(Clone)]
50pub struct Bzip2 {
51 pub level: i32, // 1-9
52 pub progress_args: ProgressArgs,
53}
54
55impl Default for Bzip2 {
56 fn default() -> Self {
57 let validator = Bzip2CompressionValidator;
58 Bzip2 {
59 level: validator.default_level(),
60 progress_args: ProgressArgs::default(),
61 }
62 }
63}
64
65impl Bzip2 {
66 pub fn new(args: &Bzip2Args) -> Self {
67 Bzip2 {
68 level: args.level_args.resolve(&Bzip2CompressionValidator),
69 progress_args: args.progress_args,
70 }
71 }
72}
73
74impl Compressor for Bzip2 {
75 /// Default extension for bzip2 files
76 fn extension(&self) -> &str {
77 "bz2"
78 }
79
80 /// Name of this compressor
81 fn name(&self) -> &str {
82 "bzip2"
83 }
84
85 /// Compress an input file or pipe to a bz2 archive
86 fn compress(&self, input: CmprssInput, output: CmprssOutput) -> Result {
87 guard_file_output(&output, "Bzip2")?;
88 let (input_stream, file_size, pipeline_inner) = open_input(input, "Bzip2")?;
89 let (writer, target) = prepare_output(output)?;
90 let mut encoder = BzEncoder::new(writer, Compression::new(self.level as u32));
91 copy_stream(
92 input_stream,
93 &mut encoder,
94 file_size,
95 pipeline_inner,
96 &self.progress_args,
97 target,
98 )?;
99 Ok(())
100 }
101
102 /// Extract a bz2 archive to a file or pipe. Unlike most decoders,
103 /// `BzDecoder` is write-driven: it wraps the output writer and we feed
104 /// compressed bytes into it.
105 fn extract(&self, input: CmprssInput, output: CmprssOutput) -> Result {
106 guard_file_output(&output, "Bzip2")?;
107 let (input_stream, file_size, pipeline_inner) = open_input(input, "Bzip2")?;
108 let (writer, target) = prepare_output(output)?;
109 let mut decoder = BzDecoder::new(writer);
110 copy_stream(
111 input_stream,
112 &mut decoder,
113 file_size,
114 pipeline_inner,
115 &self.progress_args,
116 target,
117 )?;
118 Ok(())
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use crate::test_utils::*;
126
127 /// Test the basic interface of the Bzip2 compressor
128 #[test]
129 fn test_bzip2_interface() {
130 let compressor = Bzip2::default();
131 test_compressor_interface(&compressor, "bzip2", Some("bz2"));
132 }
133
134 #[test]
135 fn test_bzip2_compression_validator() {
136 let validator = Bzip2CompressionValidator;
137 test_compression_validator_helper(
138 &validator,
139 1, // min_level
140 9, // max_level
141 9, // default_level
142 Some(1), // fast_name_level
143 Some(9), // best_name_level
144 None, // none_name_level
145 );
146 }
147
148 /// Test the default compression level
149 #[test]
150 fn test_bzip2_default_compression() -> Result {
151 let compressor = Bzip2::default();
152 test_compression(&compressor)
153 }
154
155 /// Test fast compression level
156 #[test]
157 fn test_bzip2_fast_compression() -> Result {
158 let fast_compressor = Bzip2 {
159 level: 1,
160 progress_args: ProgressArgs::default(),
161 };
162 test_compression(&fast_compressor)
163 }
164
165 /// Test best compression level
166 #[test]
167 fn test_bzip2_best_compression() -> Result {
168 let best_compressor = Bzip2 {
169 level: 9,
170 progress_args: ProgressArgs::default(),
171 };
172 test_compression(&best_compressor)
173 }
174}