this repo has no description
1use super::stream::{guard_file_output, open_input, open_output};
2use crate::{
3 progress::{ProgressArgs, copy_with_progress},
4 utils::{
5 CmprssInput, CmprssOutput, CommonArgs, CompressionLevelValidator, Compressor,
6 ExtractedTarget, LevelArgs, Result,
7 },
8};
9use bzip2::Compression;
10use bzip2::write::{BzDecoder, BzEncoder};
11use clap::Args;
12use std::io;
13
14/// BZip2-specific compression validator (1-9 range)
15#[derive(Debug, Clone, Copy)]
16pub struct Bzip2CompressionValidator;
17
18impl CompressionLevelValidator for Bzip2CompressionValidator {
19 fn min_level(&self) -> i32 {
20 1
21 }
22 fn max_level(&self) -> i32 {
23 9
24 }
25 fn default_level(&self) -> i32 {
26 9
27 }
28
29 fn name_to_level(&self, name: &str) -> Option<i32> {
30 match name.to_lowercase().as_str() {
31 "fast" => Some(1),
32 "best" => Some(9),
33 _ => None,
34 }
35 }
36}
37
38#[derive(Args, Debug)]
39pub struct Bzip2Args {
40 #[clap(flatten)]
41 pub common_args: CommonArgs,
42
43 #[clap(flatten)]
44 pub progress_args: ProgressArgs,
45
46 #[clap(flatten)]
47 pub level_args: LevelArgs,
48}
49
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 /// Bzip2 extracts to a file by default
86 fn default_extracted_target(&self) -> ExtractedTarget {
87 ExtractedTarget::File
88 }
89
90 /// Compress an input file or pipe to a bz2 archive
91 fn compress(&self, input: CmprssInput, output: CmprssOutput) -> Result {
92 guard_file_output(&output, "Bzip2")?;
93 let (mut input_stream, file_size) = open_input(input, "Bzip2")?;
94 let level = Compression::new(self.level as u32);
95
96 if let CmprssOutput::Writer(writer) = output {
97 let mut encoder = BzEncoder::new(writer, level);
98 io::copy(&mut input_stream, &mut encoder)?;
99 } else {
100 let output_stream = open_output(&output)?;
101 let mut encoder = BzEncoder::new(output_stream, level);
102 copy_with_progress(
103 &mut input_stream,
104 &mut encoder,
105 self.progress_args.chunk_size.size_in_bytes,
106 file_size,
107 self.progress_args.progress,
108 &output,
109 )?;
110 }
111
112 Ok(())
113 }
114
115 /// Extract a bz2 archive to a file or pipe
116 fn extract(&self, input: CmprssInput, output: CmprssOutput) -> Result {
117 guard_file_output(&output, "Bzip2")?;
118 let (mut input_stream, file_size) = open_input(input, "Bzip2")?;
119
120 if let CmprssOutput::Writer(writer) = output {
121 let mut decoder = BzDecoder::new(writer);
122 io::copy(&mut input_stream, &mut decoder)?;
123 } else {
124 let output_stream = open_output(&output)?;
125 let mut decoder = BzDecoder::new(output_stream);
126 copy_with_progress(
127 &mut input_stream,
128 &mut decoder,
129 self.progress_args.chunk_size.size_in_bytes,
130 file_size,
131 self.progress_args.progress,
132 &output,
133 )?;
134 }
135
136 Ok(())
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use crate::test_utils::*;
144
145 /// Test the basic interface of the Bzip2 compressor
146 #[test]
147 fn test_bzip2_interface() {
148 let compressor = Bzip2::default();
149 test_compressor_interface(&compressor, "bzip2", Some("bz2"));
150 }
151
152 #[test]
153 fn test_bzip2_compression_validator() {
154 let validator = Bzip2CompressionValidator;
155 test_compression_validator_helper(
156 &validator,
157 1, // min_level
158 9, // max_level
159 9, // default_level
160 Some(1), // fast_name_level
161 Some(9), // best_name_level
162 None, // none_name_level
163 );
164 }
165
166 /// Test the default compression level
167 #[test]
168 fn test_bzip2_default_compression() -> Result {
169 let compressor = Bzip2::default();
170 test_compression(&compressor)
171 }
172
173 /// Test fast compression level
174 #[test]
175 fn test_bzip2_fast_compression() -> Result {
176 let fast_compressor = Bzip2 {
177 level: 1,
178 progress_args: ProgressArgs::default(),
179 };
180 test_compression(&fast_compressor)
181 }
182
183 /// Test best compression level
184 #[test]
185 fn test_bzip2_best_compression() -> Result {
186 let best_compressor = Bzip2 {
187 level: 9,
188 progress_args: ProgressArgs::default(),
189 };
190 test_compression(&best_compressor)
191 }
192}