rinex/
version.rs

1//! `RINEX` revision description
2use crate::prelude::ParsingError;
3
4/// Version is used to describe RINEX standards revisions.
5#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
6#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7pub struct Version {
8    /// Version major number
9    pub major: u8,
10    /// Version minor number
11    pub minor: u8,
12}
13
14impl Default for Version {
15    /// Builds a default `Version` object
16    fn default() -> Self {
17        Version::new(4, 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::VersionFormat)?;
86
87                let minor = digits.next().ok_or(ParsingError::VersionFormat)?;
88
89                let major = major.parse::<u8>().or(Err(ParsingError::VersionParsing))?;
90                let minor = minor.parse::<u8>().or(Err(ParsingError::VersionParsing))?;
91
92                Ok(Self { major, minor })
93            },
94            false => {
95                let major = digits.next().ok_or(ParsingError::VersionFormat)?;
96
97                let major = major.parse::<u8>().or(Err(ParsingError::VersionParsing))?;
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    /// Builds desired major [Version] with minor = 0
112    pub fn from_major(major: u8) -> Self {
113        Self { major, minor: 0 }
114    }
115
116    /// Copies and returns [Version] with updated major
117    pub fn with_major(mut self, major: u8) -> Self {
118        self.major = major;
119        self
120    }
121
122    /// Copies and returns [Version] with updated minor
123    pub fn with_minor(mut self, minor: u8) -> Self {
124        self.minor = minor;
125        self
126    }
127}
128
129#[cfg(test)]
130mod test {
131    use super::*;
132    use std::str::FromStr;
133
134    #[test]
135    fn version() {
136        let version = Version::from_str("1");
137        assert!(version.is_ok());
138        let version = version.unwrap();
139        assert_eq!(version.major, 1);
140        assert_eq!(version.minor, 0);
141
142        let version = Version::from_str("1.2");
143        assert!(version.is_ok());
144        let version = version.unwrap();
145        assert_eq!(version.major, 1);
146        assert_eq!(version.minor, 2);
147
148        let version = Version::from_str("3.02");
149        assert!(version.is_ok());
150        let version = version.unwrap();
151        assert_eq!(version.major, 3);
152        assert_eq!(version.minor, 2);
153
154        let version = Version::from_str("a.b");
155        assert!(version.is_err());
156    }
157
158    #[test]
159    fn version_comparison() {
160        let v_a = Version::from_str("1.2").unwrap();
161        let v_b = Version::from_str("3.02").unwrap();
162        assert!(v_b > v_a);
163        assert!(v_b != v_a);
164    }
165
166    #[test]
167    fn version_arithmetics() {
168        let version = Version::new(3, 2);
169        assert_eq!(version + 1, Version::new(4, 2));
170        assert_eq!(version + 2, Version::new(5, 2));
171        assert_eq!(version - 2, Version::new(1, 2));
172        assert_eq!(version - 3, Version::new(1, 2)); // clamped
173
174        let (maj, min): (u8, u8) = version.into();
175        assert_eq!(maj, 3);
176        assert_eq!(min, 2);
177    }
178}