1use crate::parsers::modular;
2use crate::{BaseVersionParser, FullVersion, ParserError};
3use std::fmt;
4
5#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
19pub struct BaseVersion {
20 pub major: u64,
26 pub minor: u64,
32}
33
34impl BaseVersion {
35 pub fn new(major: u64, minor: u64) -> Self {
41 Self { major, minor }
42 }
43
44 pub fn parse(input: &str) -> Result<Self, ParserError> {
48 modular::ModularParser.parse_base(input)
49 }
50
51 pub fn to_full_version_lossy(self) -> FullVersion {
56 FullVersion {
57 major: self.major,
58 minor: self.minor,
59 patch: 0,
60 }
61 }
62
63 pub fn map<U, F>(self, fun: F) -> U
80 where
81 F: FnOnce(Self) -> U,
82 {
83 fun(self)
84 }
85}
86
87impl From<(u64, u64)> for BaseVersion {
88 fn from(tuple: (u64, u64)) -> Self {
89 BaseVersion {
90 major: tuple.0,
91 minor: tuple.1,
92 }
93 }
94}
95
96impl fmt::Display for BaseVersion {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 f.write_fmt(format_args!("{}.{}", self.major, self.minor))
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use crate::{BaseVersion, FullVersion};
105
106 #[test]
107 fn from_tuple() {
108 let major = 0;
109 let minor = 1;
110
111 assert_eq!(
112 BaseVersion { major, minor },
113 BaseVersion::from((major, minor))
114 );
115 }
116
117 #[yare::parameterized(
118 zeros = { BaseVersion { major: 0, minor: 0 }, "0.0" },
119 non_zero = { BaseVersion { major: 1, minor: 2 }, "1.2" },
120 )]
121 fn display(base_version: BaseVersion, expected: &str) {
122 let displayed = format!("{}", base_version);
123
124 assert_eq!(&displayed, expected);
125 }
126
127 #[yare::parameterized(
128 instance_0 = { BaseVersion::new(1, 0) },
129 instance_1 = { BaseVersion::new(1, 1) },
130 instance_m = { BaseVersion::new(1, u64::MAX) },
131 )]
132 fn to_full_version_lossy(base: BaseVersion) {
133 let converted = base.to_full_version_lossy();
134
135 assert_eq!(
136 converted,
137 FullVersion {
138 major: base.major,
139 minor: base.minor,
140 patch: 0,
141 }
142 )
143 }
144
145 #[test]
146 fn map() {
147 let version = BaseVersion::new(1, 2);
148 let mapped = version.map(|v| format!("Wowsies {}", v.major));
149
150 assert_eq!(mapped.as_str(), "Wowsies 1");
151 }
152}
153
154#[cfg(test)]
155mod ord_tests {
156 use crate::BaseVersion;
157 use std::cmp::Ordering;
158
159 #[yare::parameterized(
160 zero = { BaseVersion { major: 0, minor: 0 }, BaseVersion { major: 0, minor: 0 } },
161 ones = { BaseVersion { major: 1, minor: 1 }, BaseVersion { major: 1, minor: 1 } },
162 )]
163 fn equals(lhs: BaseVersion, rhs: BaseVersion) {
164 assert_eq!(lhs.cmp(&rhs), Ordering::Equal);
165 }
166
167 #[yare::parameterized(
168 minor_by_1 = { BaseVersion { major: 0, minor: 0 }, BaseVersion { major: 0, minor: 1 } },
169 major_by_1 = { BaseVersion { major: 1, minor: 0 }, BaseVersion { major: 2, minor: 0 } },
170 )]
171 fn less(lhs: BaseVersion, rhs: BaseVersion) {
172 assert_eq!(lhs.cmp(&rhs), Ordering::Less);
173 }
174
175 #[yare::parameterized(
176 minor_by_1 = { BaseVersion { major: 0, minor: 1 }, BaseVersion { major: 0, minor: 0 } },
177 major_by_1 = { BaseVersion { major: 1, minor: 0 }, BaseVersion { major: 0, minor: 0 } },
178 )]
179 fn greater(lhs: BaseVersion, rhs: BaseVersion) {
180 assert_eq!(lhs.cmp(&rhs), Ordering::Greater);
181 }
182}
183
184#[cfg(test)]
185mod partial_ord_tests {
186 use crate::BaseVersion;
187 use std::cmp::Ordering;
188
189 #[yare::parameterized(
190 zero = { BaseVersion { major: 0, minor: 0 }, BaseVersion { major: 0, minor: 0 } },
191 ones = { BaseVersion { major: 1, minor: 1 }, BaseVersion { major: 1, minor: 1 } },
192 )]
193 fn equals(lhs: BaseVersion, rhs: BaseVersion) {
194 assert_eq!(lhs.partial_cmp(&rhs), Some(Ordering::Equal));
195 }
196
197 #[yare::parameterized(
198 minor_by_1 = { BaseVersion { major: 0, minor: 0 }, BaseVersion { major: 0, minor: 1 } },
199 major_by_1 = { BaseVersion { major: 1, minor: 0 }, BaseVersion { major: 2, minor: 0 } },
200 )]
201 fn less(lhs: BaseVersion, rhs: BaseVersion) {
202 assert_eq!(lhs.partial_cmp(&rhs), Some(Ordering::Less));
203 }
204
205 #[yare::parameterized(
206 minor_by_1 = { BaseVersion { major: 0, minor: 1 }, BaseVersion { major: 0, minor: 0 } },
207 major_by_1 = { BaseVersion { major: 1, minor: 0 }, BaseVersion { major: 0, minor: 0 } },
208 )]
209 fn greater(lhs: BaseVersion, rhs: BaseVersion) {
210 assert_eq!(lhs.partial_cmp(&rhs), Some(Ordering::Greater));
211 }
212}
213
214#[cfg(test)]
215mod parse_base {
216 use crate::parsers::error::ExpectedError;
217 use crate::parsers::NumericError;
218 use crate::{BaseVersion, ParserError};
219
220 #[test]
221 fn ok() {
222 let version = BaseVersion::parse("1.2").unwrap();
223
224 assert_eq!(version, BaseVersion::new(1, 2));
225 }
226
227 #[test]
228 fn err_on_major_only() {
229 let result = BaseVersion::parse("1");
230
231 assert!(matches!(
232 result.unwrap_err(),
233 ParserError::Expected(ExpectedError::Separator { .. })
234 ));
235 }
236
237 #[test]
238 fn err_on_not_finished() {
239 let result = BaseVersion::parse("1.2.3");
240
241 assert!(matches!(
242 result.unwrap_err(),
243 ParserError::Expected(ExpectedError::EndOfInput { .. })
244 ));
245 }
246
247 #[test]
248 fn err_on_starts_with_0() {
249 let result = BaseVersion::parse("1.02");
250
251 assert!(matches!(
252 result.unwrap_err(),
253 ParserError::Numeric(NumericError::LeadingZero)
254 ));
255 }
256}