libgir/
version.rs

1use std::{
2    fmt::{self, Display, Formatter},
3    str::FromStr,
4};
5
6/// Major, minor and patch version
7#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
8pub struct Version(u16, u16, u16, bool);
9
10impl Version {
11    pub fn new(major: u16, minor: u16, patch: u16) -> Self {
12        Self(major, minor, patch, true)
13    }
14
15    /// Convert a version number to a config guard
16    ///
17    /// When generating a builder pattern, properties could be from a super-type
18    /// class/interface and so the version used there must be prefixed with
19    /// the crate name from where the super-type originates from in case it
20    /// is different from the main crate. For those cases you can pass
21    /// the crate name as the `prefix` parameter
22    pub fn to_cfg(self, prefix: Option<&str>) -> String {
23        if self.3 {
24            if let Some(p) = prefix {
25                format!("feature = \"{}_{}\"", p, self.to_feature())
26            } else {
27                format!("feature = \"{}\"", self.to_feature())
28            }
29        } else if let Some(p) = prefix {
30            format!("not(feature = \"{}_{}\")", p, self.to_feature())
31        } else {
32            format!("not(feature = \"{}\")", self.to_feature())
33        }
34    }
35
36    pub fn as_opposite(&mut self) {
37        self.3 = !self.3;
38    }
39
40    pub fn to_feature(self) -> String {
41        match self {
42            Self(major, 0, 0, _) => format!("v{major}"),
43            Self(major, minor, 0, _) => format!("v{major}_{minor}"),
44            Self(major, minor, patch, _) => format!("v{major}_{minor}_{patch}"),
45        }
46    }
47
48    /// Returns `inner_version` if it is stricter than `outer_version`, `None`
49    /// otherwise
50    pub fn if_stricter_than(
51        inner_version: Option<Self>,
52        outer_version: Option<Self>,
53    ) -> Option<Self> {
54        match (inner_version, outer_version) {
55            (Some(inner_version), Some(outer_version)) if inner_version <= outer_version => None,
56            (inner_version, _) => inner_version,
57        }
58    }
59}
60
61impl FromStr for Version {
62    type Err = String;
63
64    /// Parse a `Version` from a string.
65    /// Currently always return Ok
66    fn from_str(s: &str) -> Result<Self, String> {
67        if s.contains('.') {
68            let mut parts = s
69                .splitn(4, '.')
70                .map(str::parse)
71                .take_while(Result::is_ok)
72                .map(Result::unwrap);
73            Ok(Self::new(
74                parts.next().unwrap_or(0),
75                parts.next().unwrap_or(0),
76                parts.next().unwrap_or(0),
77            ))
78        } else {
79            let val = s.parse::<u16>();
80            Ok(Self::new(val.unwrap_or(0), 0, 0))
81        }
82    }
83}
84
85impl Display for Version {
86    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
87        match *self {
88            Self(major, 0, 0, _) => write!(f, "{major}"),
89            Self(major, minor, 0, _) => write!(f, "{major}.{minor}"),
90            Self(major, minor, patch, _) => write!(f, "{major}.{minor}.{patch}"),
91        }
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use std::str::FromStr;
98
99    use super::Version;
100
101    #[test]
102    fn from_str_works() {
103        assert_eq!(FromStr::from_str("1"), Ok(Version::new(1, 0, 0)));
104        assert_eq!(FromStr::from_str("2.1"), Ok(Version::new(2, 1, 0)));
105        assert_eq!(FromStr::from_str("3.2.1"), Ok(Version::new(3, 2, 1)));
106        assert_eq!(FromStr::from_str("3.ff.1"), Ok(Version::new(3, 0, 0)));
107    }
108
109    #[test]
110    fn parse_works() {
111        assert_eq!("1".parse(), Ok(Version::new(1, 0, 0)));
112    }
113
114    #[test]
115    fn ord() {
116        assert!(Version::new(0, 0, 0) < Version::new(1, 2, 3));
117        assert!(Version::new(1, 0, 0) < Version::new(1, 2, 3));
118        assert!(Version::new(1, 2, 0) < Version::new(1, 2, 3));
119        assert!(Version::new(1, 2, 3) == Version::new(1, 2, 3));
120        assert!(Version::new(1, 0, 0) < Version::new(2, 0, 0));
121        assert!(Version::new(3, 0, 0) == Version::new(3, 0, 0));
122    }
123}