1use crate::prelude::ParsingError;
3
4#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
6#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7pub struct Version {
8 pub major: u8,
10 pub minor: u8,
12}
13
14impl Default for Version {
15 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 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 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 pub fn new(major: u8, minor: u8) -> Self {
108 Self { major, minor }
109 }
110
111 pub fn from_major(major: u8) -> Self {
113 Self { major, minor: 0 }
114 }
115
116 pub fn with_major(mut self, major: u8) -> Self {
118 self.major = major;
119 self
120 }
121
122 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)); let (maj, min): (u8, u8) = version.into();
175 assert_eq!(maj, 3);
176 assert_eq!(min, 2);
177 }
178}