alpm_types/
compression.rs

1//! File compression related types.
2
3use std::{
4    path::{Path, PathBuf},
5    str::FromStr,
6};
7
8use serde::{Deserialize, Serialize};
9use strum::{AsRefStr, Display, EnumString, IntoStaticStr, VariantNames};
10
11/// The file extension of a compression algorithm.
12///
13/// Compression may be used for a set of different files in the ALPM context (e.g. [alpm-package],
14/// alpm-source-package, alpm-repo-database).
15/// Each algorithm uses a distinct file extension.
16///
17/// [alpm-package]: https://alpm.archlinux.page/specifications/alpm-package.7.html
18#[derive(
19    AsRefStr,
20    Clone,
21    Copy,
22    Debug,
23    Default,
24    Deserialize,
25    Display,
26    EnumString,
27    Eq,
28    IntoStaticStr,
29    PartialEq,
30    Serialize,
31    VariantNames,
32)]
33#[serde(untagged)]
34pub enum CompressionAlgorithmFileExtension {
35    /// The file extension for files compressed using the [compress] compression algorithm.
36    ///
37    /// [compress]: https://man.archlinux.org/man/compress.1
38    #[serde(rename = "Z")]
39    #[strum(to_string = "Z")]
40    Compress,
41
42    /// The file extension for files compressed using the [bzip2] compression algorithm.
43    ///
44    /// [bzip2]: https://man.archlinux.org/man/bzip2.1
45    #[serde(rename = "bz2")]
46    #[strum(to_string = "bz2")]
47    Bzip2,
48
49    /// The file extension for files compressed using the [gzip] compression algorithm.
50    ///
51    /// [gzip]: https://man.archlinux.org/man/gzip.1
52    #[serde(rename = "gz")]
53    #[strum(to_string = "gz")]
54    Gzip,
55
56    /// The file extension for files compressed using the [lrzip] compression algorithm.
57    ///
58    /// [lrzip]: https://man.archlinux.org/man/lrzip.1
59    #[serde(rename = "lrz")]
60    #[strum(to_string = "lrz")]
61    Lrzip,
62
63    /// The file extension for files compressed using the [lzip] compression algorithm.
64    ///
65    /// [lzip]: https://man.archlinux.org/man/lzip.1
66    #[serde(rename = "lz")]
67    #[strum(to_string = "lz")]
68    Lzip,
69
70    /// The file extension for files compressed using the [lz4] compression algorithm.
71    ///
72    /// [lz4]: https://man.archlinux.org/man/lz4.1
73    #[serde(rename = "lz4")]
74    #[strum(to_string = "lz4")]
75    Lz4,
76
77    /// The file extension for files compressed using the [lzop] compression algorithm.
78    ///
79    /// [lzop]: https://man.archlinux.org/man/lzop.1
80    #[serde(rename = "lzo")]
81    #[strum(to_string = "lzo")]
82    Lzop,
83
84    /// The file extension for files compressed using the [xz] compression algorithm.
85    ///
86    /// [xz]: https://man.archlinux.org/man/xz.1
87    #[serde(rename = "xz")]
88    #[strum(to_string = "xz")]
89    Xz,
90
91    /// The file extension for files compressed using the [zstd] compression algorithm.
92    ///
93    /// [zstd]: https://man.archlinux.org/man/zstd.1
94    #[default]
95    #[serde(rename = "zst")]
96    #[strum(to_string = "zst")]
97    Zstd,
98}
99
100impl TryFrom<&Path> for CompressionAlgorithmFileExtension {
101    type Error = crate::Error;
102
103    /// Creates a [`CompressionAlgorithmFileExtension`] from a [`Path`] by extracting the file
104    /// extension.
105    ///
106    /// # Errors
107    ///
108    /// Returns an error if the file extension does not match a
109    /// [`CompressionAlgorithmFileExtension`] variant.
110    fn try_from(path: &Path) -> Result<Self, Self::Error> {
111        path.extension()
112            .and_then(|ext| ext.to_str())
113            .and_then(|ext| Self::from_str(ext).ok())
114            .ok_or(strum::ParseError::VariantNotFound.into())
115    }
116}
117
118impl TryFrom<PathBuf> for CompressionAlgorithmFileExtension {
119    type Error = crate::Error;
120
121    /// Creates a [`CompressionAlgorithmFileExtension`] from a [`PathBuf`] by extracting the file
122    /// extension.
123    ///
124    /// Delegates to [`TryFrom<&Path>`][`TryFrom::try_from`].
125    ///
126    /// # Errors
127    ///
128    /// Returns an error if the file extension does not match a
129    /// [`CompressionAlgorithmFileExtension`] variant.
130    fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
131        path.as_path().try_into()
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use rstest::*;
138
139    use super::*;
140
141    #[rstest]
142    #[case("Z", CompressionAlgorithmFileExtension::Compress)]
143    #[case("bz2", CompressionAlgorithmFileExtension::Bzip2)]
144    #[case("gz", CompressionAlgorithmFileExtension::Gzip)]
145    #[case("lrz", CompressionAlgorithmFileExtension::Lrzip)]
146    #[case("lz", CompressionAlgorithmFileExtension::Lzip)]
147    #[case("lz4", CompressionAlgorithmFileExtension::Lz4)]
148    #[case("lzo", CompressionAlgorithmFileExtension::Lzop)]
149    #[case("xz", CompressionAlgorithmFileExtension::Xz)]
150    #[case("zst", CompressionAlgorithmFileExtension::Zstd)]
151    fn compression_algorithm_file_extension_from_str(
152        #[case] input: &str,
153        #[case] expected: CompressionAlgorithmFileExtension,
154    ) {
155        let parsed = CompressionAlgorithmFileExtension::from_str(input).unwrap();
156        assert_eq!(parsed, expected);
157    }
158
159    #[rstest]
160    #[case("archive.Z", CompressionAlgorithmFileExtension::Compress)]
161    #[case("data.bz2", CompressionAlgorithmFileExtension::Bzip2)]
162    #[case("doc.gz", CompressionAlgorithmFileExtension::Gzip)]
163    #[case("video.lrz", CompressionAlgorithmFileExtension::Lrzip)]
164    #[case("binary.lz", CompressionAlgorithmFileExtension::Lzip)]
165    #[case("dump.lz4", CompressionAlgorithmFileExtension::Lz4)]
166    #[case("image.lzo", CompressionAlgorithmFileExtension::Lzop)]
167    #[case("package.xz", CompressionAlgorithmFileExtension::Xz)]
168    #[case("/var/cache/repo.zst", CompressionAlgorithmFileExtension::Zstd)]
169    fn compression_algorithm_file_extension_try_from_path(
170        #[case] filename: &str,
171        #[case] expected: CompressionAlgorithmFileExtension,
172    ) -> testresult::TestResult {
173        let path = PathBuf::from(filename);
174        let parsed = CompressionAlgorithmFileExtension::try_from(path)?;
175        assert_eq!(parsed, expected);
176        Ok(())
177    }
178
179    #[rstest]
180    #[case("file.txt")]
181    #[case("unknown.abc")]
182    #[case("noext")]
183    fn invalid_compression_file_extension(#[case] filename: &str) {
184        let path = Path::new(filename);
185        let error = CompressionAlgorithmFileExtension::try_from(path).unwrap_err();
186        assert!(matches!(error, crate::Error::InvalidVariant(_)));
187    }
188}