this repo has no description
1use crate::{
2 progress::{ProgressArgs, copy_with_progress},
3 utils::*,
4};
5use anyhow::bail;
6use clap::Args;
7use std::{
8 fs::File,
9 io::{self, BufReader, BufWriter, Read, Write},
10};
11use xz2::read::XzDecoder;
12use xz2::write::XzEncoder;
13
14#[derive(Args, Debug)]
15pub struct XzArgs {
16 #[clap(flatten)]
17 pub common_args: CommonArgs,
18
19 #[clap(flatten)]
20 progress_args: ProgressArgs,
21
22 #[clap(flatten)]
23 pub level_args: LevelArgs,
24}
25
26pub struct Xz {
27 pub level: i32,
28 pub progress_args: ProgressArgs,
29}
30
31impl Default for Xz {
32 fn default() -> Self {
33 let validator = DefaultCompressionValidator;
34 Xz {
35 level: validator.default_level(),
36 progress_args: ProgressArgs::default(),
37 }
38 }
39}
40
41impl Xz {
42 pub fn new(args: &XzArgs) -> Xz {
43 let validator = DefaultCompressionValidator;
44 let level = validator.validate_and_clamp_level(args.level_args.level.level);
45
46 Xz {
47 level,
48 progress_args: args.progress_args,
49 }
50 }
51}
52
53impl Compressor for Xz {
54 /// The standard extension for the xz format.
55 fn extension(&self) -> &str {
56 "xz"
57 }
58
59 /// Full name for xz.
60 fn name(&self) -> &str {
61 "xz"
62 }
63
64 fn compress(&self, input: CmprssInput, output: CmprssOutput) -> Result {
65 let mut file_size = None;
66 let mut input_stream = match input {
67 CmprssInput::Path(paths) => {
68 if paths.len() > 1 {
69 bail!("Multiple input files not supported for xz");
70 }
71 let path = &paths[0];
72 file_size = Some(std::fs::metadata(path)?.len());
73 Box::new(BufReader::new(File::open(path)?)) as Box<dyn Read + Send>
74 }
75 CmprssInput::Pipe(pipe) => Box::new(pipe) as Box<dyn Read + Send>,
76 CmprssInput::Reader(reader) => reader.0,
77 };
78 if let CmprssOutput::Writer(writer) = output {
79 let mut encoder = XzEncoder::new(writer, self.level as u32);
80 io::copy(&mut input_stream, &mut encoder)?;
81 encoder.finish()?;
82 } else {
83 let output_stream: Box<dyn Write + Send> = match &output {
84 CmprssOutput::Path(path) => Box::new(BufWriter::new(File::create(path)?)),
85 CmprssOutput::Pipe(pipe) => Box::new(pipe) as Box<dyn Write + Send>,
86 CmprssOutput::Writer(_) => unreachable!(),
87 };
88 let mut encoder = XzEncoder::new(output_stream, self.level as u32);
89 copy_with_progress(
90 &mut input_stream,
91 &mut encoder,
92 self.progress_args.chunk_size.size_in_bytes,
93 file_size,
94 self.progress_args.progress,
95 &output,
96 )?;
97 }
98
99 Ok(())
100 }
101
102 fn extract(&self, input: CmprssInput, output: CmprssOutput) -> Result {
103 let mut file_size = None;
104 let input_stream: Box<dyn Read + Send> = match input {
105 CmprssInput::Path(paths) => {
106 if paths.len() > 1 {
107 bail!("Multiple input files not supported for xz extraction");
108 }
109 let path = &paths[0];
110 file_size = Some(std::fs::metadata(path)?.len());
111 Box::new(BufReader::new(File::open(path)?)) as Box<dyn Read + Send>
112 }
113 CmprssInput::Pipe(pipe) => Box::new(pipe) as Box<dyn Read + Send>,
114 CmprssInput::Reader(reader) => reader.0,
115 };
116 let mut decoder = XzDecoder::new(input_stream);
117
118 if let CmprssOutput::Writer(mut writer) = output {
119 io::copy(&mut decoder, &mut writer)?;
120 } else {
121 let mut output_stream: Box<dyn Write + Send> = match &output {
122 CmprssOutput::Path(path) => Box::new(BufWriter::new(File::create(path)?)),
123 CmprssOutput::Pipe(pipe) => Box::new(pipe) as Box<dyn Write + Send>,
124 CmprssOutput::Writer(_) => unreachable!(),
125 };
126 copy_with_progress(
127 &mut decoder,
128 &mut *output_stream,
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 Xz compressor
146 #[test]
147 fn test_xz_interface() {
148 let compressor = Xz::default();
149 test_compressor_interface(&compressor, "xz", Some("xz"));
150 }
151
152 /// Test the default compression level
153 #[test]
154 fn test_xz_default_compression() -> Result {
155 let compressor = Xz::default();
156 test_compression(&compressor)
157 }
158
159 /// Test fast compression level
160 #[test]
161 fn test_xz_fast_compression() -> Result {
162 let fast_compressor = Xz {
163 level: 1,
164 progress_args: ProgressArgs::default(),
165 };
166 test_compression(&fast_compressor)
167 }
168
169 /// Test best compression level
170 #[test]
171 fn test_xz_best_compression() -> Result {
172 let best_compressor = Xz {
173 level: 9,
174 progress_args: ProgressArgs::default(),
175 };
176 test_compression(&best_compressor)
177 }
178}