Skip to main content

browserslist/
semver.rs

1use std::{cmp::Ordering, fmt, num::ParseIntError, str::FromStr};
2
3/// Semver
4#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Debug, Copy, Clone)]
5pub struct Version(pub u16, pub u16, pub u16);
6
7impl Version {
8    #[inline]
9    pub const fn major(self) -> u16 {
10        self.0
11    }
12
13    #[inline]
14    pub const fn minor(self) -> u16 {
15        self.1
16    }
17
18    #[inline]
19    pub const fn patch(self) -> u16 {
20        self.2
21    }
22
23    pub fn parse(s: &str) -> Result<Self, ParseIntError> {
24        let mut segments = s.split('.');
25        let mut segment = || segments.next().map_or(Ok(0), str::parse);
26        Ok(Self(segment()?, segment()?, segment()?))
27    }
28
29    pub fn loose_compare(self, b: &str) -> Ordering {
30        let mut b = b.split('.');
31        let Some(first) = b.next() else {
32            return Ordering::Equal;
33        };
34        self.0.cmp(&first.parse().unwrap_or_default()).then_with(|| match b.next() {
35            Some(second) => self.1.cmp(&second.parse().unwrap_or_default()),
36            None => Ordering::Equal,
37        })
38    }
39}
40
41impl FromStr for Version {
42    type Err = ParseIntError;
43
44    fn from_str(s: &str) -> Result<Self, Self::Err> {
45        // this allows something like `4.4.3-4.4.4`
46        let s = s.split_once('-').map_or(s, |(v, _)| v);
47        Self::parse(s)
48    }
49}
50
51impl fmt::Display for Version {
52    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53        write!(f, "{}.{}.{}", self.0, self.1, self.2)
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60
61    #[test]
62    fn parse_version() {
63        assert_eq!(Ok(Version(1, 0, 0)), "1".parse());
64        assert_eq!(Ok(Version(1, 2, 0)), "1.2".parse());
65        assert_eq!(Ok(Version(1, 2, 3)), "1.2.3".parse());
66        assert_eq!(Ok(Version(12, 34, 56)), "12.34.56".parse());
67
68        assert_eq!(Ok(Version(1, 0, 0)), "1-2".parse());
69        assert_eq!(Ok(Version(1, 2, 0)), "1.2-1.3".parse());
70        assert_eq!(Ok(Version(1, 2, 3)), "1.2.3-1.2.4".parse());
71        assert_eq!(Ok(Version(12, 34, 56)), "12.34.56-78.9".parse());
72
73        assert!("tp".parse::<Version>().is_err());
74    }
75}