alpm_compress/compression/
level.rs

1//! Compression level structs for various compression algorithms.
2
3use std::fmt::{Debug, Display};
4
5use log::trace;
6
7use crate::error::Error;
8
9/// A macro to define a compression level struct.
10///
11/// Accepts the `name` of the compression level struct, its `min`, `max` and `default` values, the
12/// `compression` executable it relates to and a `url`, that defines a man page for the
13/// `compression` executable.
14macro_rules! define_compression_level {
15    (
16        $name:ident,
17        Min => $min:expr,
18        Max => $max:expr,
19        Default => $default:expr,
20        $compression:literal,
21        $url:literal
22    ) => {
23        #[doc = concat!("Compression level for ", $compression, " compression.")]
24        #[derive(Clone, Debug, Eq, PartialEq)]
25        pub struct $name(u8);
26
27        impl $name {
28            #[doc = concat!("Creates a new [`", stringify!($name), "`] from a [`u8`].")]
29            ///
30            #[doc = concat!("The `level` must be in the range of [`", stringify!($name), "::min`] and [`", stringify!($name), "::max`].")]
31            ///
32            /// # Errors
33            ///
34            #[doc = concat!("Returns an error if the value is not in the range of [`", stringify!($name), "::min`] and [`", stringify!($name), "::max`].")]
35            pub fn new(level: u8) -> Result<Self, Error> {
36                trace!(concat!("Creating new compression level for ", $compression, " compression with {{level}}"));
37                if !($name::min()..=$name::max()).contains(&level) {
38                    return Err(Error::InvalidCompressionLevel {
39                        level,
40                        min: $name::min(),
41                        max: $name::max(),
42                    });
43                }
44                Ok(Self(level))
45            }
46
47            #[doc = concat!("Returns the default level (`", stringify!($default), "`) for [`", stringify!($name), "`].")]
48            ///
49            #[doc = concat!("The default level adheres to the one selected by the [", $compression, "] executable.")]
50            ///
51            #[doc = concat!("[", $compression, "]: ", $url)]
52            pub const fn default_level() -> u8 {
53                $default
54            }
55
56            #[doc = concat!("Returns the minimum allowed level (`", stringify!($min), "`) for [`", stringify!($name), "`].")]
57            pub const fn min() -> u8 {
58                $min
59            }
60
61            #[doc = concat!("Returns the maximum allowed level (`", stringify!($max), "`) for [`", stringify!($name), "`].")]
62            pub const fn max() -> u8 {
63                $max
64            }
65        }
66
67        impl Default for $name {
68            #[doc = concat!("Returns the default [`", stringify!($name), "`].")]
69            ///
70            #[doc = concat!("Delegates to [`", stringify!($name), "::default_level`] for retrieving the default compression level.")]
71            fn default() -> Self {
72                Self($name::default_level())
73            }
74        }
75
76        impl Display for $name {
77            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78                write!(f, "{}", self.0)
79            }
80        }
81
82        impl From<&$name> for i32 {
83            fn from(value: &$name) -> Self {
84                i32::from(value.0)
85            }
86        }
87
88        impl From<&$name> for u32 {
89            fn from(value: &$name) -> Self {
90                 u32::from(value.0)
91            }
92        }
93
94        impl TryFrom<i8> for $name {
95            type Error = Error;
96
97            fn try_from(value: i8) -> Result<Self, Error> {
98                 $name::new(u8::try_from(value).map_err(Error::IntegerConversion)?)
99            }
100        }
101
102        impl TryFrom<i16> for $name {
103            type Error = Error;
104
105            fn try_from(value: i16) -> Result<Self, Error> {
106                 $name::new(u8::try_from(value).map_err(Error::IntegerConversion)?)
107            }
108        }
109
110        impl TryFrom<i32> for $name {
111            type Error = Error;
112
113            fn try_from(value: i32) -> Result<Self, Error> {
114                 $name::new(u8::try_from(value).map_err(Error::IntegerConversion)?)
115            }
116        }
117
118        impl TryFrom<i64> for $name {
119            type Error = Error;
120
121            fn try_from(value: i64) -> Result<Self, Error> {
122                 $name::new(u8::try_from(value).map_err(Error::IntegerConversion)?)
123            }
124        }
125
126        impl TryFrom<u8> for $name {
127            type Error = Error;
128
129            fn try_from(value: u8) -> Result<Self, Error> {
130                 $name::new(value)
131            }
132        }
133
134        impl TryFrom<u16> for $name {
135            type Error = Error;
136
137            fn try_from(value: u16) -> Result<Self, Error> {
138                 $name::new(u8::try_from(value).map_err(Error::IntegerConversion)?)
139            }
140        }
141
142        impl TryFrom<u32> for $name {
143            type Error = Error;
144
145            fn try_from(value: u32) -> Result<Self, Error> {
146                 $name::new(u8::try_from(value).map_err(Error::IntegerConversion)?)
147            }
148        }
149
150        impl TryFrom<u64> for $name {
151            type Error = Error;
152
153            fn try_from(value: u64) -> Result<Self, Error> {
154                 $name::new(u8::try_from(value).map_err(Error::IntegerConversion)?)
155            }
156        }
157    };
158}
159
160// Create the bzip2 compression level struct.
161define_compression_level!(
162    Bzip2CompressionLevel,
163    Min => 1,
164    Max => 9,
165    Default => 9,
166    "bzip2",
167    "https://man.archlinux.org/man/bzip2.1"
168);
169
170// Create the gzip compression level struct.
171define_compression_level!(
172    GzipCompressionLevel,
173    Min => 1,
174    Max => 9,
175    Default => 6,
176    "gzip",
177    "https://man.archlinux.org/man/gzip.1"
178);
179
180// Create the xz compression level struct.
181define_compression_level!(
182    XzCompressionLevel,
183    Min => 0,
184    Max => 9,
185    Default => 6,
186    "xz",
187    "https://man.archlinux.org/man/xz.1"
188);
189
190// Create the zstd compression level struct.
191define_compression_level!(
192    ZstdCompressionLevel,
193    Min => 0,
194    Max => 22,
195    Default => 3,
196    "zstd",
197    "https://man.archlinux.org/man/zstd.1"
198);
199
200#[cfg(test)]
201mod tests {
202    use proptest::{proptest, test_runner::Config as ProptestConfig};
203    use rstest::rstest;
204    use testresult::TestResult;
205
206    use super::*;
207
208    proptest! {
209        #![proptest_config(ProptestConfig::with_cases(1000))]
210
211        #[test]
212        fn valid_bzip2_compression_level_try_from_i8(input in 1..=9i8) {
213            assert!(Bzip2CompressionLevel::try_from(input).is_ok());
214        }
215
216        #[test]
217        fn valid_bzip2_compression_level_try_from_i16(input in 1..=9i16) {
218            assert!(Bzip2CompressionLevel::try_from(input).is_ok());
219        }
220
221        #[test]
222        fn valid_bzip2_compression_level_try_from_i32(input in 1..=9i32) {
223            assert!(Bzip2CompressionLevel::try_from(input).is_ok());
224        }
225
226        #[test]
227        fn valid_bzip2_compression_level_try_from_i64(input in 1..=9i64) {
228            assert!(Bzip2CompressionLevel::try_from(input).is_ok());
229        }
230
231        #[test]
232        fn valid_bzip2_compression_level_try_from_u8(input in 1..=9u8) {
233            assert!(Bzip2CompressionLevel::try_from(input).is_ok());
234        }
235
236        #[test]
237        fn valid_bzip2_compression_level_try_from_u16(input in 1..=9u16) {
238            assert!(Bzip2CompressionLevel::try_from(input).is_ok());
239        }
240
241        #[test]
242        fn valid_bzip2_compression_level_try_from_u32(input in 1..=9u32) {
243            assert!(Bzip2CompressionLevel::try_from(input).is_ok());
244        }
245
246        #[test]
247        fn valid_bzip2_compression_level_try_from_u64(input in 1..=9u64) {
248            assert!(Bzip2CompressionLevel::try_from(input).is_ok());
249        }
250
251        #[test]
252        fn valid_gzip_compression_level_try_from_i8(input in 1..=9i8) {
253            assert!(GzipCompressionLevel::try_from(input).is_ok());
254        }
255
256        #[test]
257        fn valid_gzip_compression_level_try_from_i16(input in 1..=9i16) {
258            assert!(GzipCompressionLevel::try_from(input).is_ok());
259        }
260
261        #[test]
262        fn valid_gzip_compression_level_try_from_i32(input in 1..=9i32) {
263            assert!(GzipCompressionLevel::try_from(input).is_ok());
264        }
265
266        #[test]
267        fn valid_gzip_compression_level_try_from_i64(input in 1..=9i64) {
268            assert!(GzipCompressionLevel::try_from(input).is_ok());
269        }
270
271        #[test]
272        fn valid_gzip_compression_level_try_from_u8(input in 1..=9u8) {
273            assert!(GzipCompressionLevel::try_from(input).is_ok());
274        }
275
276        #[test]
277        fn valid_gzip_compression_level_try_from_u16(input in 1..=9u16) {
278            assert!(GzipCompressionLevel::try_from(input).is_ok());
279        }
280
281        #[test]
282        fn valid_gzip_compression_level_try_from_u32(input in 1..=9u32) {
283            assert!(GzipCompressionLevel::try_from(input).is_ok());
284        }
285
286        #[test]
287        fn valid_gzip_compression_level_try_from_u64(input in 1..=9u64) {
288            assert!(GzipCompressionLevel::try_from(input).is_ok());
289        }
290
291        #[test]
292        fn valid_xz_compression_level_try_from_i8(input in 0..=9i8) {
293            assert!(XzCompressionLevel::try_from(input).is_ok());
294        }
295
296        #[test]
297        fn valid_xz_compression_level_try_from_i16(input in 0..=9i16) {
298            assert!(XzCompressionLevel::try_from(input).is_ok());
299        }
300
301        #[test]
302        fn valid_xz_compression_level_try_from_i32(input in 0..=9i32) {
303            assert!(XzCompressionLevel::try_from(input).is_ok());
304        }
305
306        #[test]
307        fn valid_xz_compression_level_try_from_i64(input in 0..=9i64) {
308            assert!(XzCompressionLevel::try_from(input).is_ok());
309        }
310
311        #[test]
312        fn valid_xz_compression_level_try_from_u8(input in 0..=9u8) {
313            assert!(XzCompressionLevel::try_from(input).is_ok());
314        }
315
316        #[test]
317        fn valid_xz_compression_level_try_from_u16(input in 0..=9u16) {
318            assert!(XzCompressionLevel::try_from(input).is_ok());
319        }
320
321        #[test]
322        fn valid_xz_compression_level_try_from_u32(input in 0..=9u32) {
323            assert!(XzCompressionLevel::try_from(input).is_ok());
324        }
325
326        #[test]
327        fn valid_xz_compression_level_try_from_u64(input in 0..=9u64) {
328            assert!(XzCompressionLevel::try_from(input).is_ok());
329        }
330
331        #[test]
332        fn valid_zstd_compression_level_try_from_i8(input in 0..=22i8) {
333            assert!(ZstdCompressionLevel::try_from(input).is_ok());
334        }
335
336        #[test]
337        fn valid_zstd_compression_level_try_from_i16(input in 0..=22i16) {
338            assert!(ZstdCompressionLevel::try_from(input).is_ok());
339        }
340
341        #[test]
342        fn valid_zstd_compression_level_try_from_i32(input in 0..=22i32) {
343            assert!(ZstdCompressionLevel::try_from(input).is_ok());
344        }
345
346        #[test]
347        fn valid_zstd_compression_level_try_from_i64(input in 0..=22i64) {
348            assert!(ZstdCompressionLevel::try_from(input).is_ok());
349        }
350
351        #[test]
352        fn valid_zstd_compression_level_try_from_u8(input in 0..=22u8) {
353            assert!(ZstdCompressionLevel::try_from(input).is_ok());
354        }
355
356        #[test]
357        fn valid_zstd_compression_level_try_from_u16(input in 0..=22u16) {
358            assert!(ZstdCompressionLevel::try_from(input).is_ok());
359        }
360
361        #[test]
362        fn valid_zstd_compression_level_try_from_u32(input in 0..=22u32) {
363            assert!(ZstdCompressionLevel::try_from(input).is_ok());
364        }
365
366        #[test]
367        fn valid_zstd_compression_level_try_from_u64(input in 0..=22u64) {
368            assert!(ZstdCompressionLevel::try_from(input).is_ok());
369        }
370    }
371
372    #[rstest]
373    #[case::too_large(Bzip2CompressionLevel::max() + 1)]
374    #[case::too_small(Bzip2CompressionLevel::min() - 1)]
375    fn create_bzip2_compression_level_fails(#[case] level: u8) -> TestResult {
376        if let Ok(level) = Bzip2CompressionLevel::new(level) {
377            return Err(format!("Should not have succeeded but created level: {level}").into());
378        }
379
380        Ok(())
381    }
382
383    #[test]
384    fn create_bzip2_compression_level_succeeds() -> TestResult {
385        if let Err(error) = Bzip2CompressionLevel::new(6) {
386            return Err(format!("Should have succeeded but raised error:\n{error}").into());
387        }
388
389        Ok(())
390    }
391
392    #[rstest]
393    #[case::too_large(GzipCompressionLevel::max() + 1)]
394    #[case::too_small(GzipCompressionLevel::min() - 1)]
395    fn create_gzip_compression_level_fails(#[case] level: u8) -> TestResult {
396        if let Ok(level) = GzipCompressionLevel::new(level) {
397            return Err(format!("Should not have succeeded but created level: {level}").into());
398        }
399
400        Ok(())
401    }
402
403    #[test]
404    fn create_gzip_compression_level_succeeds() -> TestResult {
405        if let Err(error) = GzipCompressionLevel::new(6) {
406            return Err(format!("Should have succeeded but raised error:\n{error}").into());
407        }
408
409        Ok(())
410    }
411
412    #[test]
413    fn create_xz_compression_level_fails() -> TestResult {
414        if let Ok(level) = XzCompressionLevel::new(XzCompressionLevel::max() + 1) {
415            return Err(format!("Should not have succeeded but created level: {level}").into());
416        }
417
418        Ok(())
419    }
420
421    #[test]
422    fn create_xz_compression_level_succeeds() -> TestResult {
423        if let Err(error) = XzCompressionLevel::new(6) {
424            return Err(format!("Should have succeeded but raised error:\n{error}").into());
425        }
426
427        Ok(())
428    }
429
430    #[test]
431    fn create_zstd_compression_level_fails() -> TestResult {
432        if let Ok(level) = ZstdCompressionLevel::new(ZstdCompressionLevel::max() + 1) {
433            return Err(format!("Should not have succeeded but created level: {level}").into());
434        }
435
436        Ok(())
437    }
438
439    #[test]
440    fn create_zstd_compression_level_succeeds() -> TestResult {
441        if let Err(error) = ZstdCompressionLevel::new(6) {
442            return Err(format!("Should have succeeded but raised error:\n{error}").into());
443        }
444
445        Ok(())
446    }
447}