Skip to main content

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
16/// Converts user compression level (1-9) to flate2 compression level.
17///
18/// # Mapping
19///
20/// - `None` or `Some(6)`: Default compression
21/// - `1-3`: Fast compression
22/// - `7-9`: Best compression
23/// - Other values: Literal level (clamped to valid range)
24///
25/// # Examples
26///
27/// ```ignore
28/// use exarch_core::creation::compression::compression_level_to_flate2;
29///
30/// let default_level = compression_level_to_flate2(None);
31/// let fast_level = compression_level_to_flate2(Some(1));
32/// let best_level = compression_level_to_flate2(Some(9));
33/// ```
34#[must_use]
35pub fn compression_level_to_flate2(level: Option<u8>) -> flate2::Compression {
36    match level {
37        None | Some(6) => flate2::Compression::default(),
38        Some(1..=3) => flate2::Compression::fast(),
39        Some(7..=9) => flate2::Compression::best(),
40        Some(n) => flate2::Compression::new(u32::from(n)),
41    }
42}
43
44/// Converts user compression level (1-9) to bzip2 compression level.
45///
46/// # Mapping
47///
48/// - `None` or `Some(6)`: Default compression
49/// - `1`: Fast compression
50/// - `2-6`: Literal level
51/// - `7-9`: Best compression
52///
53/// # Examples
54///
55/// ```ignore
56/// use exarch_core::creation::compression::compression_level_to_bzip2;
57///
58/// let default_level = compression_level_to_bzip2(None);
59/// let fast_level = compression_level_to_bzip2(Some(1));
60/// let best_level = compression_level_to_bzip2(Some(9));
61/// ```
62#[must_use]
63pub fn compression_level_to_bzip2(level: Option<u8>) -> bzip2::Compression {
64    match level {
65        None | Some(6) => bzip2::Compression::default(),
66        Some(1) => bzip2::Compression::fast(),
67        Some(7..=9) => bzip2::Compression::best(),
68        Some(n @ 2..=6) => bzip2::Compression::new(u32::from(n)),
69        Some(n) => bzip2::Compression::new(u32::from(n.min(9))),
70    }
71}
72
73/// Converts user compression level (1-9) to xz compression level.
74///
75/// # Mapping
76///
77/// - `None` or `Some(6)`: Level 6 (default)
78/// - Other values: Literal level (0-9 range supported by xz)
79///
80/// # Examples
81///
82/// ```ignore
83/// use exarch_core::creation::compression::compression_level_to_xz;
84///
85/// let default_level = compression_level_to_xz(None);
86/// let fast_level = compression_level_to_xz(Some(1));
87/// let best_level = compression_level_to_xz(Some(9));
88/// ```
89#[must_use]
90pub fn compression_level_to_xz(level: Option<u8>) -> u32 {
91    match level {
92        None | Some(6) => 6,
93        Some(n) => u32::from(n),
94    }
95}
96
97/// Converts user compression level (1-9) to zstd compression level.
98///
99/// # Mapping
100///
101/// Zstd has a wider range (1-22) than our user scale (1-9).
102/// We map user levels to strategic zstd levels:
103///
104/// - `None` or `Some(6)`: Level 3 (default, fast with good compression)
105/// - `1`: Level 1 (fastest)
106/// - `2`: Level 2 (fast)
107/// - `7`: Level 10 (good compression)
108/// - `8`: Level 15 (better compression)
109/// - `9`: Level 19 (best compression)
110/// - Other values: Level 3 (default)
111///
112/// # Examples
113///
114/// ```ignore
115/// use exarch_core::creation::compression::compression_level_to_zstd;
116///
117/// let default_level = compression_level_to_zstd(None);
118/// assert_eq!(default_level, 3);
119///
120/// let fast_level = compression_level_to_zstd(Some(1));
121/// assert_eq!(fast_level, 1);
122///
123/// let best_level = compression_level_to_zstd(Some(9));
124/// assert_eq!(best_level, 19);
125/// ```
126#[allow(clippy::match_same_arms)] // Different semantic meanings for each level
127#[must_use]
128pub fn compression_level_to_zstd(level: Option<u8>) -> i32 {
129    match level {
130        // Default compression level
131        None | Some(6) => 3,
132        Some(1) => 1,
133        Some(2) => 2,
134        Some(7) => 10,
135        Some(8) => 15,
136        Some(9) => 19,
137        // All other levels (3-5, 0, 10+) map to default
138        _ => 3,
139    }
140}
141
142/// Unified compression level type.
143///
144/// This enum wraps codec-specific compression level types to provide
145/// a unified interface for compression level conversion.
146#[derive(Debug, Clone, Copy)]
147pub enum CompressionLevel {
148    /// Flate2 (gzip) compression level.
149    Flate2(flate2::Compression),
150
151    /// Bzip2 compression level.
152    Bzip2(bzip2::Compression),
153
154    /// Xz compression level (raw u32).
155    Xz(u32),
156
157    /// Zstd compression level (raw i32).
158    Zstd(i32),
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn test_compression_level_to_flate2() {
167        // Default
168        let level = compression_level_to_flate2(None);
169        assert_eq!(level, flate2::Compression::default());
170
171        let level = compression_level_to_flate2(Some(6));
172        assert_eq!(level, flate2::Compression::default());
173
174        // Fast
175        let level = compression_level_to_flate2(Some(1));
176        assert_eq!(level, flate2::Compression::fast());
177
178        // Best
179        let level = compression_level_to_flate2(Some(9));
180        assert_eq!(level, flate2::Compression::best());
181
182        // Specific level
183        let level = compression_level_to_flate2(Some(5));
184        assert_eq!(level, flate2::Compression::new(5));
185    }
186
187    #[test]
188    fn test_compression_level_to_bzip2() {
189        // Default
190        let level = compression_level_to_bzip2(None);
191        assert_eq!(level, bzip2::Compression::default());
192
193        // Fast
194        let level = compression_level_to_bzip2(Some(1));
195        assert_eq!(level, bzip2::Compression::fast());
196
197        // Best
198        let level = compression_level_to_bzip2(Some(9));
199        assert_eq!(level, bzip2::Compression::best());
200
201        // Specific level
202        let level = compression_level_to_bzip2(Some(4));
203        assert_eq!(level, bzip2::Compression::new(4));
204    }
205
206    #[test]
207    fn test_compression_level_to_xz() {
208        assert_eq!(compression_level_to_xz(None), 6);
209        assert_eq!(compression_level_to_xz(Some(6)), 6);
210        assert_eq!(compression_level_to_xz(Some(1)), 1);
211        assert_eq!(compression_level_to_xz(Some(9)), 9);
212    }
213
214    #[test]
215    fn test_compression_level_to_zstd() {
216        assert_eq!(compression_level_to_zstd(None), 3);
217        assert_eq!(compression_level_to_zstd(Some(6)), 3);
218        assert_eq!(compression_level_to_zstd(Some(1)), 1);
219        assert_eq!(compression_level_to_zstd(Some(2)), 2);
220        assert_eq!(compression_level_to_zstd(Some(7)), 10);
221        assert_eq!(compression_level_to_zstd(Some(8)), 15);
222        assert_eq!(compression_level_to_zstd(Some(9)), 19);
223    }
224}