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