browserslist/
semver.rs

1use std::{cmp::Ordering, num::ParseIntError, str::FromStr};
2
3#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Debug, Clone)]
4pub(crate) struct Version(u32, u32, u32);
5
6impl Version {
7    #[inline]
8    pub(crate) fn major(&self) -> u32 {
9        self.0
10    }
11}
12
13impl FromStr for Version {
14    type Err = ParseIntError;
15
16    fn from_str(s: &str) -> Result<Self, Self::Err> {
17        // this allows something like `4.4.3-4.4.4`
18        let mut segments = s.split_once('-').map(|(v, _)| v).unwrap_or(s).split('.');
19        let major = match segments.next() {
20            Some(n) => n.parse()?,
21            None => 0,
22        };
23        let minor = match segments.next() {
24            Some(n) => n.parse()?,
25            None => 0,
26        };
27        let patch = match segments.next() {
28            Some(n) => n.parse()?,
29            None => 0,
30        };
31
32        Ok(Self(major, minor, patch))
33    }
34}
35
36pub(crate) fn compare(a: &str, b: &str) -> Ordering {
37    a.parse::<Version>()
38        .unwrap_or_default()
39        .cmp(&b.parse().unwrap_or_default())
40}
41
42pub(crate) fn loose_compare(a: &str, b: &str) -> Ordering {
43    a.split('.')
44        .take(2)
45        .zip(b.split('.').take(2))
46        .fold(Ordering::Equal, |ord, (a, b)| {
47            if ord == Ordering::Equal {
48                a.parse::<i32>()
49                    .unwrap_or_default()
50                    .cmp(&b.parse::<i32>().unwrap_or_default())
51            } else {
52                ord
53            }
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}