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