doris_rs/header/
version.rs

1use crate::prelude::ParsingError;
2
3/// [Version] describes DORIS files revision.
4#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
5#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6pub struct Version {
7    /// Version major number
8    pub major: u8,
9
10    /// Version minor number
11    pub minor: u8,
12}
13
14impl Default for Version {
15    /// Builds a default 3.0 DORIS [Version]
16    fn default() -> Self {
17        Version::new(3, 0)
18    }
19}
20
21impl std::fmt::Display for Version {
22    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
23        write!(f, "{}.{}", self.major, self.minor)
24    }
25}
26
27impl std::ops::Add<u8> for Version {
28    type Output = Version;
29    fn add(self, major: u8) -> Version {
30        Version {
31            major: self.major + major,
32            minor: self.minor,
33        }
34    }
35}
36
37impl std::ops::AddAssign<u8> for Version {
38    fn add_assign(&mut self, major: u8) {
39        self.major += major;
40    }
41}
42
43impl std::ops::Sub<u8> for Version {
44    type Output = Version;
45    fn sub(self, major: u8) -> Version {
46        if major >= self.major {
47            // clamp @ V1.X
48            Version {
49                major: 1,
50                minor: self.minor,
51            }
52        } else {
53            Version {
54                major: self.major - major,
55                minor: self.minor,
56            }
57        }
58    }
59}
60
61impl std::ops::SubAssign<u8> for Version {
62    fn sub_assign(&mut self, major: u8) {
63        if major >= self.major {
64            // clamp @ V1.X
65            self.major = 1;
66        } else {
67            self.major -= major;
68        }
69    }
70}
71
72impl From<Version> for (u8, u8) {
73    fn from(v: Version) -> Self {
74        (v.major, v.minor)
75    }
76}
77
78impl std::str::FromStr for Version {
79    type Err = ParsingError;
80    fn from_str(s: &str) -> Result<Self, Self::Err> {
81        let mut digits = s.split('.');
82
83        match s.contains('.') {
84            true => {
85                let major = digits.next().ok_or(ParsingError::Version)?;
86
87                let minor = digits.next().ok_or(ParsingError::Version)?;
88
89                let major = major.parse::<u8>().or(Err(ParsingError::Version))?;
90                let minor = minor.parse::<u8>().or(Err(ParsingError::Version))?;
91
92                Ok(Self { major, minor })
93            },
94            false => {
95                let major = digits.next().ok_or(ParsingError::Version)?;
96
97                let major = major.parse::<u8>().or(Err(ParsingError::Version))?;
98
99                Ok(Self { major, minor: 0 })
100            },
101        }
102    }
103}
104
105impl Version {
106    /// Builds a new [Version]
107    pub fn new(major: u8, minor: u8) -> Self {
108        Self { major, minor }
109    }
110}
111
112#[cfg(test)]
113mod test {
114    use super::*;
115    use std::str::FromStr;
116
117    #[test]
118    fn version() {
119        let version = Version::from_str("1");
120        assert!(version.is_ok());
121        let version = version.unwrap();
122        assert_eq!(version.major, 1);
123        assert_eq!(version.minor, 0);
124
125        let version = Version::from_str("1.2");
126        assert!(version.is_ok());
127        let version = version.unwrap();
128        assert_eq!(version.major, 1);
129        assert_eq!(version.minor, 2);
130
131        let version = Version::from_str("3.02");
132        assert!(version.is_ok());
133        let version = version.unwrap();
134        assert_eq!(version.major, 3);
135        assert_eq!(version.minor, 2);
136
137        let version = Version::from_str("a.b");
138        assert!(version.is_err());
139    }
140
141    #[test]
142    fn version_comparison() {
143        let v_a = Version::from_str("1.2").unwrap();
144        let v_b = Version::from_str("3.02").unwrap();
145        assert!(v_b > v_a);
146        assert!(v_b != v_a);
147    }
148
149    #[test]
150    fn version_arithmetics() {
151        let version = Version::new(3, 2);
152        assert_eq!(version + 1, Version::new(4, 2));
153        assert_eq!(version + 2, Version::new(5, 2));
154        assert_eq!(version - 2, Version::new(1, 2));
155        assert_eq!(version - 3, Version::new(1, 2)); // clamped
156
157        let (maj, min): (u8, u8) = version.into();
158        assert_eq!(maj, 3);
159        assert_eq!(min, 2);
160    }
161}