alpm_types/
compression.rs1use std::{
4 path::{Path, PathBuf},
5 str::FromStr,
6};
7
8use alpm_parsers::{iter_str_context, traits::AlpmParser};
9use serde::{Deserialize, Serialize};
10use strum::{AsRefStr, Display, EnumString, IntoStaticStr, VariantNames};
11use winnow::{
12 Parser,
13 ascii::alphanumeric1,
14 error::{ContextError, ErrMode, StrContext, StrContextValue},
15};
16
17#[derive(
25 AsRefStr,
26 Clone,
27 Copy,
28 Debug,
29 Default,
30 Deserialize,
31 Display,
32 EnumString,
33 Eq,
34 IntoStaticStr,
35 PartialEq,
36 Serialize,
37 VariantNames,
38)]
39#[serde(untagged)]
40pub enum CompressionAlgorithmFileExtension {
41 #[serde(rename = "Z")]
45 #[strum(to_string = "Z")]
46 Compress,
47
48 #[serde(rename = "bz2")]
52 #[strum(to_string = "bz2")]
53 Bzip2,
54
55 #[serde(rename = "gz")]
59 #[strum(to_string = "gz")]
60 Gzip,
61
62 #[serde(rename = "lrz")]
66 #[strum(to_string = "lrz")]
67 Lrzip,
68
69 #[serde(rename = "lz")]
73 #[strum(to_string = "lz")]
74 Lzip,
75
76 #[serde(rename = "lz4")]
80 #[strum(to_string = "lz4")]
81 Lz4,
82
83 #[serde(rename = "lzo")]
87 #[strum(to_string = "lzo")]
88 Lzop,
89
90 #[serde(rename = "xz")]
94 #[strum(to_string = "xz")]
95 Xz,
96
97 #[default]
101 #[serde(rename = "zst")]
102 #[strum(to_string = "zst")]
103 Zstd,
104}
105
106impl AlpmParser for CompressionAlgorithmFileExtension {
107 fn parser(input: &mut &str) -> Result<Self, ErrMode<ContextError>> {
114 alphanumeric1
115 .try_map(CompressionAlgorithmFileExtension::from_str)
116 .context(StrContext::Label("compression algorithm file extension"))
117 .context_with(iter_str_context!([
118 CompressionAlgorithmFileExtension::VARIANTS
119 ]))
120 .parse_next(input)
121 }
122
123 fn delimiter_error_context<'a, O, P>(
124 parser: P,
125 ) -> impl Parser<&'a str, O, ErrMode<ContextError>>
126 where
127 P: Parser<&'a str, O, ErrMode<ContextError>>,
128 {
129 parser
130 .context(StrContext::Label("compression algorithm file extension"))
131 .context(StrContext::Expected(StrContextValue::Description(
132 "an alphanumeric string",
133 )))
134 }
135}
136
137impl TryFrom<&Path> for CompressionAlgorithmFileExtension {
138 type Error = crate::Error;
139
140 fn try_from(path: &Path) -> Result<Self, Self::Error> {
148 path.extension()
149 .and_then(|ext| ext.to_str())
150 .and_then(|ext| Self::from_str(ext).ok())
151 .ok_or(strum::ParseError::VariantNotFound.into())
152 }
153}
154
155impl TryFrom<PathBuf> for CompressionAlgorithmFileExtension {
156 type Error = crate::Error;
157
158 fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
168 path.as_path().try_into()
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use rstest::*;
175
176 use super::*;
177
178 #[rstest]
179 #[case("Z", CompressionAlgorithmFileExtension::Compress)]
180 #[case("bz2", CompressionAlgorithmFileExtension::Bzip2)]
181 #[case("gz", CompressionAlgorithmFileExtension::Gzip)]
182 #[case("lrz", CompressionAlgorithmFileExtension::Lrzip)]
183 #[case("lz", CompressionAlgorithmFileExtension::Lzip)]
184 #[case("lz4", CompressionAlgorithmFileExtension::Lz4)]
185 #[case("lzo", CompressionAlgorithmFileExtension::Lzop)]
186 #[case("xz", CompressionAlgorithmFileExtension::Xz)]
187 #[case("zst", CompressionAlgorithmFileExtension::Zstd)]
188 fn compression_algorithm_file_extension_from_str(
189 #[case] input: &str,
190 #[case] expected: CompressionAlgorithmFileExtension,
191 ) {
192 let parsed = CompressionAlgorithmFileExtension::from_str(input).unwrap();
193 assert_eq!(parsed, expected);
194 }
195
196 #[rstest]
197 #[case("archive.Z", CompressionAlgorithmFileExtension::Compress)]
198 #[case("data.bz2", CompressionAlgorithmFileExtension::Bzip2)]
199 #[case("doc.gz", CompressionAlgorithmFileExtension::Gzip)]
200 #[case("video.lrz", CompressionAlgorithmFileExtension::Lrzip)]
201 #[case("binary.lz", CompressionAlgorithmFileExtension::Lzip)]
202 #[case("dump.lz4", CompressionAlgorithmFileExtension::Lz4)]
203 #[case("image.lzo", CompressionAlgorithmFileExtension::Lzop)]
204 #[case("package.xz", CompressionAlgorithmFileExtension::Xz)]
205 #[case("/var/cache/repo.zst", CompressionAlgorithmFileExtension::Zstd)]
206 fn compression_algorithm_file_extension_try_from_path(
207 #[case] filename: &str,
208 #[case] expected: CompressionAlgorithmFileExtension,
209 ) -> testresult::TestResult {
210 let path = PathBuf::from(filename);
211 let parsed = CompressionAlgorithmFileExtension::try_from(path)?;
212 assert_eq!(parsed, expected);
213 Ok(())
214 }
215
216 #[rstest]
217 #[case("file.txt")]
218 #[case("unknown.abc")]
219 #[case("noext")]
220 fn invalid_compression_file_extension(#[case] filename: &str) {
221 let path = Path::new(filename);
222 let error = CompressionAlgorithmFileExtension::try_from(path).unwrap_err();
223 assert!(matches!(error, crate::Error::InvalidVariant(_)));
224 }
225}