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}