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