exarch_core/creation/compression.rs
1//! Compression level conversion utilities.
2//!
3//! This module provides unified conversion from the user-friendly compression
4//! level scale (1-9) to codec-specific compression level types.
5//!
6//! # Level Mapping
7//!
8//! User levels follow a consistent scale:
9//!
10//! - **1-3**: Fast compression (lower CPU usage, larger files)
11//! - **6**: Default compression (balanced)
12//! - **7-9**: Best compression (higher CPU usage, smaller files)
13//!
14//! Each codec maps these levels to its own internal scale.
15
16use crate::formats::compression::CompressionCodec;
17
18/// Converts user compression level (1-9) to flate2 compression level.
19///
20/// # Mapping
21///
22/// - `None` or `Some(6)`: Default compression
23/// - `1-3`: Fast compression
24/// - `7-9`: Best compression
25/// - Other values: Literal level (clamped to valid range)
26///
27/// # Examples
28///
29/// ```
30/// use exarch_core::creation::compression::compression_level_to_flate2;
31///
32/// let default_level = compression_level_to_flate2(None);
33/// let fast_level = compression_level_to_flate2(Some(1));
34/// let best_level = compression_level_to_flate2(Some(9));
35/// ```
36#[must_use]
37pub fn compression_level_to_flate2(level: Option<u8>) -> flate2::Compression {
38 match level {
39 None | Some(6) => flate2::Compression::default(),
40 Some(1..=3) => flate2::Compression::fast(),
41 Some(7..=9) => flate2::Compression::best(),
42 Some(n) => flate2::Compression::new(u32::from(n)),
43 }
44}
45
46/// Converts user compression level (1-9) to bzip2 compression level.
47///
48/// # Mapping
49///
50/// - `None` or `Some(6)`: Default compression
51/// - `1`: Fast compression
52/// - `2-6`: Literal level
53/// - `7-9`: Best compression
54///
55/// # Examples
56///
57/// ```
58/// use exarch_core::creation::compression::compression_level_to_bzip2;
59///
60/// let default_level = compression_level_to_bzip2(None);
61/// let fast_level = compression_level_to_bzip2(Some(1));
62/// let best_level = compression_level_to_bzip2(Some(9));
63/// ```
64#[must_use]
65pub fn compression_level_to_bzip2(level: Option<u8>) -> bzip2::Compression {
66 match level {
67 None | Some(6) => bzip2::Compression::default(),
68 Some(1) => bzip2::Compression::fast(),
69 Some(7..=9) => bzip2::Compression::best(),
70 Some(n @ 2..=6) => bzip2::Compression::new(u32::from(n)),
71 Some(n) => bzip2::Compression::new(u32::from(n.min(9))),
72 }
73}
74
75/// Converts user compression level (1-9) to xz compression level.
76///
77/// # Mapping
78///
79/// - `None` or `Some(6)`: Level 6 (default)
80/// - Other values: Literal level (0-9 range supported by xz)
81///
82/// # Examples
83///
84/// ```
85/// use exarch_core::creation::compression::compression_level_to_xz;
86///
87/// let default_level = compression_level_to_xz(None);
88/// let fast_level = compression_level_to_xz(Some(1));
89/// let best_level = compression_level_to_xz(Some(9));
90/// ```
91#[must_use]
92pub fn compression_level_to_xz(level: Option<u8>) -> u32 {
93 match level {
94 None | Some(6) => 6,
95 Some(n) => u32::from(n),
96 }
97}
98
99/// Converts user compression level (1-9) to zstd compression level.
100///
101/// # Mapping
102///
103/// Zstd has a wider range (1-22) than our user scale (1-9).
104/// We map user levels to strategic zstd levels:
105///
106/// - `None` or `Some(6)`: Level 3 (default, fast with good compression)
107/// - `1`: Level 1 (fastest)
108/// - `2`: Level 2 (fast)
109/// - `7`: Level 10 (good compression)
110/// - `8`: Level 15 (better compression)
111/// - `9`: Level 19 (best compression)
112/// - Other values: Level 3 (default)
113///
114/// # Examples
115///
116/// ```
117/// use exarch_core::creation::compression::compression_level_to_zstd;
118///
119/// let default_level = compression_level_to_zstd(None);
120/// assert_eq!(default_level, 3);
121///
122/// let fast_level = compression_level_to_zstd(Some(1));
123/// assert_eq!(fast_level, 1);
124///
125/// let best_level = compression_level_to_zstd(Some(9));
126/// assert_eq!(best_level, 19);
127/// ```
128#[allow(clippy::match_same_arms)] // Different semantic meanings for each level
129#[must_use]
130pub fn compression_level_to_zstd(level: Option<u8>) -> i32 {
131 match level {
132 // Default compression level
133 None | Some(6) => 3,
134 Some(1) => 1,
135 Some(2) => 2,
136 Some(7) => 10,
137 Some(8) => 15,
138 Some(9) => 19,
139 // All other levels (3-5, 0, 10+) map to default
140 _ => 3,
141 }
142}
143
144/// Converts compression codec and level to the appropriate compression type.
145///
146/// This is a convenience function that dispatches to the codec-specific
147/// conversion functions.
148///
149/// # Type Parameters
150///
151/// The return type is an enum that wraps all possible compression types.
152/// Use pattern matching to extract the specific type.
153///
154/// # Examples
155///
156/// ```
157/// use exarch_core::creation::compression::CompressionLevel;
158/// use exarch_core::creation::compression::convert_compression_level;
159/// use exarch_core::formats::compression::CompressionCodec;
160///
161/// let level = convert_compression_level(CompressionCodec::Gzip, Some(9));
162/// match level {
163/// CompressionLevel::Flate2(c) => {
164/// // Use flate2 compression
165/// }
166/// _ => unreachable!(),
167/// }
168/// ```
169#[must_use]
170pub fn convert_compression_level(codec: CompressionCodec, level: Option<u8>) -> CompressionLevel {
171 match codec {
172 CompressionCodec::Gzip => CompressionLevel::Flate2(compression_level_to_flate2(level)),
173 CompressionCodec::Bzip2 => CompressionLevel::Bzip2(compression_level_to_bzip2(level)),
174 CompressionCodec::Xz => CompressionLevel::Xz(compression_level_to_xz(level)),
175 CompressionCodec::Zstd => CompressionLevel::Zstd(compression_level_to_zstd(level)),
176 }
177}
178
179/// Unified compression level type.
180///
181/// This enum wraps codec-specific compression level types to provide
182/// a unified interface for compression level conversion.
183#[derive(Debug, Clone, Copy)]
184pub enum CompressionLevel {
185 /// Flate2 (gzip) compression level.
186 Flate2(flate2::Compression),
187
188 /// Bzip2 compression level.
189 Bzip2(bzip2::Compression),
190
191 /// Xz compression level (raw u32).
192 Xz(u32),
193
194 /// Zstd compression level (raw i32).
195 Zstd(i32),
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_compression_level_to_flate2() {
204 // Default
205 let level = compression_level_to_flate2(None);
206 assert_eq!(level, flate2::Compression::default());
207
208 let level = compression_level_to_flate2(Some(6));
209 assert_eq!(level, flate2::Compression::default());
210
211 // Fast
212 let level = compression_level_to_flate2(Some(1));
213 assert_eq!(level, flate2::Compression::fast());
214
215 // Best
216 let level = compression_level_to_flate2(Some(9));
217 assert_eq!(level, flate2::Compression::best());
218
219 // Specific level
220 let level = compression_level_to_flate2(Some(5));
221 assert_eq!(level, flate2::Compression::new(5));
222 }
223
224 #[test]
225 fn test_compression_level_to_bzip2() {
226 // Default
227 let level = compression_level_to_bzip2(None);
228 assert_eq!(level, bzip2::Compression::default());
229
230 // Fast
231 let level = compression_level_to_bzip2(Some(1));
232 assert_eq!(level, bzip2::Compression::fast());
233
234 // Best
235 let level = compression_level_to_bzip2(Some(9));
236 assert_eq!(level, bzip2::Compression::best());
237
238 // Specific level
239 let level = compression_level_to_bzip2(Some(4));
240 assert_eq!(level, bzip2::Compression::new(4));
241 }
242
243 #[test]
244 fn test_compression_level_to_xz() {
245 assert_eq!(compression_level_to_xz(None), 6);
246 assert_eq!(compression_level_to_xz(Some(6)), 6);
247 assert_eq!(compression_level_to_xz(Some(1)), 1);
248 assert_eq!(compression_level_to_xz(Some(9)), 9);
249 }
250
251 #[test]
252 fn test_compression_level_to_zstd() {
253 assert_eq!(compression_level_to_zstd(None), 3);
254 assert_eq!(compression_level_to_zstd(Some(6)), 3);
255 assert_eq!(compression_level_to_zstd(Some(1)), 1);
256 assert_eq!(compression_level_to_zstd(Some(2)), 2);
257 assert_eq!(compression_level_to_zstd(Some(7)), 10);
258 assert_eq!(compression_level_to_zstd(Some(8)), 15);
259 assert_eq!(compression_level_to_zstd(Some(9)), 19);
260 }
261
262 #[test]
263 fn test_convert_compression_level() {
264 let level = convert_compression_level(CompressionCodec::Gzip, Some(9));
265 match level {
266 CompressionLevel::Flate2(c) => {
267 assert_eq!(c, flate2::Compression::best());
268 }
269 _ => panic!("Expected Flate2 compression level"),
270 }
271
272 let level = convert_compression_level(CompressionCodec::Zstd, None);
273 match level {
274 CompressionLevel::Zstd(c) => {
275 assert_eq!(c, 3);
276 }
277 _ => panic!("Expected Zstd compression level"),
278 }
279 }
280}