1use crate::parsers::modular;
2use crate::{BaseVersion, FullVersionParser, ParserError};
3use std::fmt;
4
5#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
28pub struct FullVersion {
29 pub major: u64,
35 pub minor: u64,
41 pub patch: u64,
43}
44
45impl FullVersion {
46 pub fn new(major: u64, minor: u64, patch: u64) -> Self {
52 Self {
53 major,
54 minor,
55 patch,
56 }
57 }
58
59 pub fn parse(input: &str) -> Result<Self, ParserError> {
63 modular::ModularParser.parse_full(input)
64 }
65
66 pub fn to_base_version_lossy(self) -> BaseVersion {
70 BaseVersion {
71 major: self.major,
72 minor: self.minor,
73 }
74 }
75
76 pub fn map<U, F>(self, fun: F) -> U
93 where
94 F: FnOnce(Self) -> U,
95 {
96 fun(self)
97 }
98}
99
100#[cfg(feature = "semver")]
101impl From<FullVersion> for semver::Version {
102 fn from(version: FullVersion) -> Self {
120 semver::Version::new(version.major, version.minor, version.patch)
121 }
122}
123
124impl From<(u64, u64, u64)> for FullVersion {
125 fn from(tuple: (u64, u64, u64)) -> Self {
126 FullVersion {
127 major: tuple.0,
128 minor: tuple.1,
129 patch: tuple.2,
130 }
131 }
132}
133
134impl fmt::Display for FullVersion {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 f.write_fmt(format_args!("{}.{}.{}", self.major, self.minor, self.patch))
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use crate::{BaseVersion, FullVersion};
143
144 #[test]
145 fn from_tuple() {
146 let major = 0;
147 let minor = 1;
148 let patch = 2;
149
150 assert_eq!(
151 FullVersion {
152 major,
153 minor,
154 patch
155 },
156 FullVersion::from((major, minor, patch))
157 );
158 }
159
160 #[yare::parameterized(
161 zeros = { FullVersion { major: 0, minor: 0, patch: 0 }, "0.0.0" },
162 non_zero = { FullVersion { major: 1, minor: 2, patch: 3 }, "1.2.3" },
163 )]
164 fn display(base_version: FullVersion, expected: &str) {
165 let displayed = format!("{}", base_version);
166
167 assert_eq!(&displayed, expected);
168 }
169
170 #[test]
171 fn to_base_version_lossy() {
172 let full = FullVersion {
173 major: 1,
174 minor: 2,
175 patch: 3,
176 };
177 let converted = full.to_base_version_lossy();
178
179 assert_eq!(BaseVersion { major: 1, minor: 2 }, converted)
180 }
181
182 #[test]
183 fn map() {
184 let version = BaseVersion::new(1, 2);
185 let mapped = version.map(|v| ("Everything is awesome", v.major, v.minor));
186
187 assert_eq!(mapped.1, 1);
188 assert_eq!(mapped.2, 2);
189 }
190}
191
192#[cfg(test)]
193mod ord_tests {
194 use crate::FullVersion;
195 use std::cmp::Ordering;
196
197 #[yare::parameterized(
198 zero = { FullVersion { major: 0, minor: 0, patch: 0 }, FullVersion { major: 0, minor: 0, patch: 0 } },
199 ones = { FullVersion { major: 1, minor: 1, patch: 1 }, FullVersion { major: 1, minor: 1, patch: 1 } },
200 )]
201 fn equals(lhs: FullVersion, rhs: FullVersion) {
202 assert_eq!(lhs.cmp(&rhs), Ordering::Equal);
203 }
204
205 #[yare::parameterized(
206 major_by_1 = { FullVersion { major: 0, minor: 0, patch: 0 }, FullVersion { major: 1, minor: 0, patch: 0 } },
207 minor_by_1 = { FullVersion { major: 0, minor: 0, patch: 0 }, FullVersion { major: 0, minor: 1, patch: 0 } },
208 patch_by_1 = { FullVersion { major: 0, minor: 0, patch: 0 }, FullVersion { major: 0, minor: 0, patch: 1 } },
209 )]
210 fn less(lhs: FullVersion, rhs: FullVersion) {
211 assert_eq!(lhs.cmp(&rhs), Ordering::Less);
212 }
213
214 #[yare::parameterized(
215 major_by_1 = { FullVersion { major: 1, minor: 0, patch: 0 }, FullVersion { major: 0, minor: 0, patch: 0 } },
216 minor_by_1 = { FullVersion { major: 0, minor: 1, patch: 0 }, FullVersion { major: 0, minor: 0, patch: 0 } },
217 patch_by_1 = { FullVersion { major: 0, minor: 0, patch: 1 }, FullVersion { major: 0, minor: 0, patch: 0 } },
218 )]
219 fn greater(lhs: FullVersion, rhs: FullVersion) {
220 assert_eq!(lhs.cmp(&rhs), Ordering::Greater);
221 }
222}
223
224#[cfg(test)]
225mod partial_ord_tests {
226 use crate::FullVersion;
227 use std::cmp::Ordering;
228
229 #[yare::parameterized(
230 zero = { FullVersion { major: 0, minor: 0, patch: 0 }, FullVersion { major: 0, minor: 0, patch: 0 } },
231 ones = { FullVersion { major: 1, minor: 1, patch: 1 }, FullVersion { major: 1, minor: 1, patch: 1 } },
232 )]
233 fn equals(lhs: FullVersion, rhs: FullVersion) {
234 assert_eq!(lhs.partial_cmp(&rhs), Some(Ordering::Equal));
235 }
236
237 #[yare::parameterized(
238 major_by_1 = { FullVersion { major: 0, minor: 0, patch: 0 }, FullVersion { major: 1, minor: 0, patch: 0 } },
239 minor_by_1 = { FullVersion { major: 0, minor: 0, patch: 0 }, FullVersion { major: 0, minor: 1, patch: 0 } },
240 patch_by_1 = { FullVersion { major: 0, minor: 0, patch: 0 }, FullVersion { major: 0, minor: 0, patch: 1 } },
241 )]
242 fn less(lhs: FullVersion, rhs: FullVersion) {
243 assert_eq!(lhs.partial_cmp(&rhs), Some(Ordering::Less));
244 }
245
246 #[yare::parameterized(
247 major_by_1 = { FullVersion { major: 1, minor: 0, patch: 0 }, FullVersion { major: 0, minor: 0, patch: 0 } },
248 minor_by_1 = { FullVersion { major: 0, minor: 1, patch: 0 }, FullVersion { major: 0, minor: 0, patch: 0 } },
249 patch_by_1 = { FullVersion { major: 0, minor: 0, patch: 1 }, FullVersion { major: 0, minor: 0, patch: 0 } },
250 )]
251 fn greater(lhs: FullVersion, rhs: FullVersion) {
252 assert_eq!(lhs.partial_cmp(&rhs), Some(Ordering::Greater));
253 }
254}
255
256#[cfg(test)]
257mod parse_full {
258 use crate::parsers::error::ExpectedError;
259 use crate::parsers::NumericError;
260 use crate::{FullVersion, ParserError};
261
262 #[test]
263 fn ok() {
264 let version = FullVersion::parse("1.2.3").unwrap();
265
266 assert_eq!(version, FullVersion::new(1, 2, 3));
267 }
268
269 #[test]
270 fn err_on_base_only() {
271 let result = FullVersion::parse("1.2");
272
273 assert!(matches!(
274 result.unwrap_err(),
275 ParserError::Expected(ExpectedError::Separator { .. })
276 ));
277 }
278
279 #[test]
280 fn err_on_not_finished() {
281 let result = FullVersion::parse("1.2.3.");
282
283 assert!(matches!(
284 result.unwrap_err(),
285 ParserError::Expected(ExpectedError::EndOfInput { .. })
286 ));
287 }
288
289 #[test]
290 fn err_on_starts_with_0() {
291 let result = FullVersion::parse("1.2.03");
292
293 assert!(matches!(
294 result.unwrap_err(),
295 ParserError::Numeric(NumericError::LeadingZero)
296 ));
297 }
298}