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