alpm_compress/compression/
level.rs1use std::fmt::{Debug, Display};
4
5use log::trace;
6
7use crate::error::Error;
8
9macro_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 #[doc = concat!("The `level` must be in the range of [`", stringify!($name), "::min`] and [`", stringify!($name), "::max`].")]
31 #[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 #[doc = concat!("The default level adheres to the one selected by the [", $compression, "] executable.")]
50 #[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 #[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
160define_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
170define_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
180define_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
190define_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}