alpm_types/
error.rs

1use std::path::PathBuf;
2
3use crate::Architecture;
4
5/// The library's error type
6///
7/// These errors are usually parsing errors and they each contain a context
8/// about why the error has occurred and the value that caused the error.
9///
10/// The original error is also included in the variants that have the `source` field.
11/// You can access it using the `source()` method.
12/// See [Error::source](https://doc.rust-lang.org/std/error/trait.Error.html#method.source) for
13/// more information.
14#[derive(Debug, thiserror::Error, PartialEq)]
15pub enum Error {
16    /// Combination of architectures that is invalid.
17    #[error("The architecture combination is invalid: {architectures:?}. {context}")]
18    InvalidArchitectures {
19        /// The invalid architectures combination.
20        architectures: Vec<Architecture>,
21        /// The reason why the architectures are invalid.
22        context: &'static str,
23    },
24
25    /// An invalid integer
26    #[error("Invalid integer (caused by {kind:?})")]
27    InvalidInteger {
28        /// The reason for the invalid integer.
29        kind: std::num::IntErrorKind,
30    },
31
32    /// An invalid enum variant
33    #[error("Invalid variant ({0})")]
34    InvalidVariant(#[from] strum::ParseError),
35
36    /// An invalid email address
37    #[error("Invalid e-mail ({0})")]
38    InvalidEmail(#[from] email_address::Error),
39
40    /// An invalid URL
41    #[error("Invalid URL ({0})")]
42    InvalidUrl(#[from] url::ParseError),
43
44    /// An invalid license
45    #[error("Invalid license ({0})")]
46    InvalidLicense(#[from] spdx::ParseError),
47
48    /// An invalid semantic version string
49    ///
50    /// This error occurs when a semantic version cannot be parsed from a string
51    /// We cannot use `#[source] semver::Error` here because it does not implement `PartialEq`.
52    /// See: <https://github.com/dtolnay/semver/issues/326>
53    ///
54    /// TODO: Use the error source when the issue above is resolved.
55    #[error("Invalid semver ({kind})")]
56    InvalidSemver {
57        /// The reason for the invalid semantic version.
58        kind: String,
59    },
60
61    /// Value contains an invalid character
62    #[error("Value contains invalid characters: {invalid_char:?}")]
63    ValueContainsInvalidChars {
64        /// The invalid character
65        invalid_char: char,
66    },
67
68    /// Value length is incorrect
69    #[error("Incorrect length, got {length} expected {expected}")]
70    IncorrectLength {
71        /// The incorrect length.
72        length: usize,
73        /// The expected length.
74        expected: usize,
75    },
76
77    /// Value is missing a delimiter character
78    #[error("Value is missing the required delimiter: {delimiter}")]
79    DelimiterNotFound {
80        /// The required delimiter.
81        delimiter: char,
82    },
83
84    /// Value does not match the restrictions
85    #[error("Does not match the restrictions ({restrictions:?})")]
86    ValueDoesNotMatchRestrictions {
87        /// The list of restrictions that cannot be met.
88        restrictions: Vec<String>,
89    },
90
91    /// A validation regex does not match the value
92    #[error("Value '{value}' does not match the '{regex_type}' regex: {regex}")]
93    RegexDoesNotMatch {
94        /// The value that does not match.
95        value: String,
96        /// The type of regular expression applied to the `value`.
97        regex_type: String,
98        /// The regular expression applied to the `value`.
99        regex: String,
100    },
101
102    /// A winnow parser for a type didn't work and produced an error.
103    #[error("Parser failed with the following error:\n{0}")]
104    ParseError(String),
105
106    /// Missing field in a value
107    #[error("Missing component: {component}")]
108    MissingComponent {
109        /// The component that is missing.
110        component: &'static str,
111    },
112
113    /// An invalid absolute path (i.e. does not start with a `/`)
114    #[error("The path is not absolute: {0}")]
115    PathNotAbsolute(PathBuf),
116
117    /// An invalid relative path (i.e. starts with a `/`)
118    #[error("The path is not relative: {0}")]
119    PathNotRelative(PathBuf),
120
121    /// File name contains invalid characters
122    #[error("File name ({0}) contains invalid characters: {1:?}")]
123    FileNameContainsInvalidChars(PathBuf, char),
124
125    /// File name is empty
126    #[error("File name is empty")]
127    FileNameIsEmpty,
128
129    /// A deprecated license
130    #[error("Deprecated license: {0}")]
131    DeprecatedLicense(String),
132
133    /// A component is invalid and cannot be used.
134    #[error("Invalid component {component} encountered while {context}")]
135    InvalidComponent {
136        /// The invalid component
137        component: &'static str,
138        /// The context in which the error occurs.
139        ///
140        /// This is meant to complete the sentence "Invalid component {component} encountered
141        /// while ".
142        context: &'static str,
143    },
144
145    /// An invalid OpenPGP v4 fingerprint
146    #[error("Invalid OpenPGP v4 fingerprint, only 40 uppercase hexadecimal characters are allowed")]
147    InvalidOpenPGPv4Fingerprint,
148
149    /// An invalid OpenPGP key ID
150    #[error("The string is not a valid OpenPGP key ID: {0}, must be 16 hexadecimal characters")]
151    InvalidOpenPGPKeyId(String),
152
153    /// An invalid shared object name (v1)
154    #[error("Invalid shared object name (v1): {0}")]
155    InvalidSonameV1(&'static str),
156
157    /// A package data error.
158    #[error("Package error: {0}")]
159    Package(#[from] crate::PackageError),
160
161    /// A string represents an unknown compression algorithm file extension.
162    #[error("Unknown compression algorithm file extension: {value:?}")]
163    UnknownCompressionAlgorithmFileExtension {
164        /// A String representing an unknown compression algorithm file extension.
165        value: String,
166    },
167
168    /// A string represents an unknown file type identifier.
169    #[error("Unknown file type identifier: {value:?}")]
170    UnknownFileTypeIdentifier {
171        /// A String representing an unknown file type identifier.
172        value: String,
173    },
174}
175
176impl From<std::num::ParseIntError> for crate::error::Error {
177    /// Converts a [`std::num::ParseIntError`] into an [`Error::InvalidInteger`].
178    fn from(e: std::num::ParseIntError) -> Self {
179        Self::InvalidInteger { kind: *e.kind() }
180    }
181}
182
183impl<'a> From<winnow::error::ParseError<&'a str, winnow::error::ContextError>>
184    for crate::error::Error
185{
186    /// Converts a [`winnow::error::ParseError`] into an [`Error::ParseError`].
187    fn from(value: winnow::error::ParseError<&'a str, winnow::error::ContextError>) -> Self {
188        Self::ParseError(value.to_string())
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use std::num::IntErrorKind;
195
196    use rstest::rstest;
197
198    use super::*;
199
200    #[rstest]
201    #[case(
202        "Invalid integer (caused by InvalidDigit)",
203        Error::InvalidInteger {
204            kind: IntErrorKind::InvalidDigit
205        }
206    )]
207    #[case(
208        "Invalid integer (caused by InvalidDigit)",
209        Error::InvalidInteger {
210            kind: IntErrorKind::InvalidDigit
211        }
212    )]
213    #[case(
214        "Invalid integer (caused by PosOverflow)",
215        Error::InvalidInteger {
216            kind: IntErrorKind::PosOverflow
217        }
218    )]
219    #[allow(deprecated)]
220    #[case(
221        "Invalid integer (caused by InvalidDigit)",
222        Error::InvalidInteger {
223            kind: IntErrorKind::InvalidDigit
224        }
225    )]
226    #[case(
227        "Invalid e-mail (Missing separator character '@'.)",
228        email_address::Error::MissingSeparator.into()
229    )]
230    #[case(
231        "Invalid integer (caused by InvalidDigit)",
232        Error::InvalidInteger {
233            kind: IntErrorKind::InvalidDigit
234        }
235    )]
236    fn error_format_string(#[case] error_str: &str, #[case] error: Error) {
237        assert_eq!(error_str, format!("{error}"));
238    }
239}