kernel_abi_check/
version.rs

1use std::{fmt::Display, str::FromStr};
2
3use eyre::{ensure, Context, Result};
4use serde::{de, Deserialize, Deserializer};
5
6/// Symbol version.
7#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
8pub struct Version(Vec<usize>);
9
10impl<'de> Deserialize<'de> for Version {
11    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
12    where
13        D: Deserializer<'de>,
14    {
15        let s = String::deserialize(deserializer)?;
16        FromStr::from_str(&s).map_err(de::Error::custom)
17    }
18}
19
20impl Display for Version {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        write!(
23            f,
24            "{}",
25            itertools::join(self.0.iter().map(|v| v.to_string()), ".")
26        )
27    }
28}
29
30impl From<Vec<usize>> for Version {
31    fn from(value: Vec<usize>) -> Self {
32        // Remove trailing zeros for normalization.
33        let mut normalized = value
34            .into_iter()
35            .rev()
36            .skip_while(|&x| x == 0)
37            .collect::<Vec<_>>();
38        normalized.reverse();
39        Version(normalized)
40    }
41}
42
43impl FromStr for Version {
44    type Err = eyre::Report;
45
46    fn from_str(version: &str) -> Result<Self, Self::Err> {
47        let version = version.trim().to_owned();
48        ensure!(!version.is_empty(), "Empty version string");
49        let mut version_parts = Vec::new();
50        for part in version.split('.') {
51            let version_part: usize = part
52                .parse()
53                .context(format!("Version must consist of numbers: {version}"))?;
54            version_parts.push(version_part);
55        }
56
57        Ok(Version::from(version_parts))
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use std::cmp::Ordering;
64
65    use crate::Version;
66
67    #[test]
68    fn test_version_equals() {
69        assert_eq!(Version::from(vec![5, 0, 0]), Version::from(vec![5, 0, 0]));
70        assert_eq!(Version::from(vec![5, 0, 0]), Version::from(vec![5]));
71        assert_eq!(Version::from(vec![5]), Version::from(vec![5, 0, 0]));
72    }
73
74    #[test]
75    fn version_ord() {
76        assert_eq!(
77            Version::from(vec![5, 0, 0]).cmp(&Version::from(vec![5, 0, 0])),
78            Ordering::Equal
79        );
80        assert_eq!(
81            Version::from(vec![5, 0, 0]).cmp(&Version::from(vec![5])),
82            Ordering::Equal
83        );
84        assert_eq!(
85            Version::from(vec![5]).cmp(&Version::from(vec![5, 0, 0])),
86            Ordering::Equal
87        );
88        assert_eq!(
89            Version::from(vec![5, 0, 0]).cmp(&Version::from(vec![5, 0, 1])),
90            Ordering::Less
91        );
92        assert_eq!(
93            Version::from(vec![5]).cmp(&Version::from(vec![5, 0, 1])),
94            Ordering::Less
95        );
96        assert_eq!(
97            Version::from(vec![5, 0, 1]).cmp(&Version::from(vec![5, 0, 0])),
98            Ordering::Greater
99        );
100        assert_eq!(
101            Version::from(vec![5, 0, 1]).cmp(&Version::from(vec![5])),
102            Ordering::Greater
103        );
104    }
105}