alpm_types/version/
schema.rs

1//! Schema version handling.
2
3use std::{
4    fmt::{Display, Formatter},
5    str::FromStr,
6};
7
8use semver::Version as SemverVersion;
9
10use crate::Error;
11
12/// The schema version of a type
13///
14/// A `SchemaVersion` wraps a `semver::Version`, which means that the tracked version should follow [semver](https://semver.org).
15/// However, for backwards compatibility reasons it is possible to initialize a `SchemaVersion`
16/// using a non-semver compatible string, *if* it can be parsed to a single `u64` (e.g. `"1"`).
17///
18/// ## Examples
19/// ```
20/// use std::str::FromStr;
21///
22/// use alpm_types::SchemaVersion;
23///
24/// # fn main() -> Result<(), alpm_types::Error> {
25/// // create SchemaVersion from str
26/// let version_one = SchemaVersion::from_str("1.0.0")?;
27/// let version_also_one = SchemaVersion::from_str("1")?;
28/// assert_eq!(version_one, version_also_one);
29///
30/// // format as String
31/// assert_eq!("1.0.0", format!("{}", version_one));
32/// assert_eq!("1.0.0", format!("{}", version_also_one));
33/// # Ok(())
34/// # }
35/// ```
36#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
37pub struct SchemaVersion(SemverVersion);
38
39impl SchemaVersion {
40    /// Create a new SchemaVersion
41    pub fn new(version: SemverVersion) -> Self {
42        SchemaVersion(version)
43    }
44
45    /// Return a reference to the inner type
46    pub fn inner(&self) -> &SemverVersion {
47        &self.0
48    }
49}
50
51impl FromStr for SchemaVersion {
52    type Err = Error;
53    /// Create a new SchemaVersion from a string
54    ///
55    /// When providing a non-semver string with only a number (i.e. no minor or patch version), the
56    /// number is treated as the major version (e.g. `"23"` -> `"23.0.0"`).
57    fn from_str(s: &str) -> Result<SchemaVersion, Self::Err> {
58        if !s.contains('.') {
59            match s.parse() {
60                Ok(major) => Ok(SchemaVersion(SemverVersion::new(major, 0, 0))),
61                Err(e) => Err(Error::InvalidInteger {
62                    kind: e.kind().clone(),
63                }),
64            }
65        } else {
66            match SemverVersion::parse(s) {
67                Ok(version) => Ok(SchemaVersion(version)),
68                Err(e) => Err(Error::InvalidSemver {
69                    kind: e.to_string(),
70                }),
71            }
72        }
73    }
74}
75
76impl Display for SchemaVersion {
77    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
78        write!(fmt, "{}", self.0)
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use rstest::rstest;
85
86    use super::*;
87
88    #[rstest]
89    #[case("1.0.0", Ok(SchemaVersion(SemverVersion::new(1, 0, 0))))]
90    #[case("1", Ok(SchemaVersion(SemverVersion::new(1, 0, 0))))]
91    #[case("-1.0.0", Err(Error::InvalidSemver { kind: String::from("unexpected character '-' while parsing major version number") }))]
92    fn schema_version(#[case] version: &str, #[case] result: Result<SchemaVersion, Error>) {
93        assert_eq!(result, SchemaVersion::from_str(version))
94    }
95
96    #[rstest]
97    #[case(
98        SchemaVersion(SemverVersion::new(1, 0, 0)),
99        SchemaVersion(SemverVersion::new(0, 1, 0))
100    )]
101    fn compare_schema_version(#[case] version_a: SchemaVersion, #[case] version_b: SchemaVersion) {
102        assert!(version_a > version_b);
103    }
104}