1use crate::parser::{self, Parser};
34use std::fmt;
35
36#[derive(Clone, PartialOrd, Ord, Hash, Debug, PartialEq, Eq)]
61pub struct Version {
62 pub major: u64,
64 pub minor: u64,
66 pub patch: u64,
68 pub pre: Vec<Identifier>,
71 pub build: Vec<Identifier>,
74}
75
76#[derive(Clone, PartialOrd, Ord, Hash, Debug, PartialEq, Eq)]
104pub enum Identifier {
105 Numeric(u64),
107 AlphaNumeric(String),
109}
110
111impl Identifier {
112 pub fn concat(self, add_str: &str) -> Identifier {
113 match self {
114 Identifier::Numeric(n) => Identifier::AlphaNumeric(format!("{}{}", n, add_str)),
115 Identifier::AlphaNumeric(s) => Identifier::AlphaNumeric(format!("{}{}", s, add_str)),
116 }
117 }
118}
119
120pub fn parse(input: &str) -> Result<Version, parser::Error> {
145 let mut parser = Parser::new(input)?;
146 let version = parser.version()?;
147
148 if !parser.is_eof() {
149 return Err(parser::Error::MoreInput(parser.tail()?));
150 }
151
152 Ok(version)
153}
154
155impl fmt::Display for Version {
156 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157 write!(f, "{}.{}.{}", self.major, self.minor, self.patch).expect("write failed");
158 if !self.pre.is_empty() {
159 let strs: Vec<_> = self.pre.iter().map(ToString::to_string).collect();
160 write!(f, "-{}", strs.join(".")).expect("write failed");
161 }
162 if !self.build.is_empty() {
163 let strs: Vec<_> = self.build.iter().map(ToString::to_string).collect();
164 write!(f, "+{}", strs.join(".")).expect("write failed");
165 }
166 Ok(())
167 }
168}
169
170impl fmt::Display for Identifier {
171 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
172 match *self {
173 Identifier::Numeric(ref id) => id.fmt(f),
174 Identifier::AlphaNumeric(ref id) => id.fmt(f),
175 }
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use crate::version;
183
184 #[test]
185 fn parse_empty() {
186 let version = "";
187
188 let parsed = version::parse(version);
189
190 assert!(
191 parsed.is_err(),
192 "empty string incorrectly considered a valid parse"
193 );
194 }
195
196 #[test]
197 fn parse_blank() {
198 let version = " ";
199
200 let parsed = version::parse(version);
201
202 assert!(
203 parsed.is_err(),
204 "blank string incorrectly considered a valid parse"
205 );
206 }
207
208 #[test]
209 fn parse_no_minor_patch() {
210 let version = "1";
211
212 let parsed = version::parse(version);
213
214 assert!(
215 parsed.is_err(),
216 "'{}' incorrectly considered a valid parse", version
217 );
218 }
219
220 #[test]
221 fn parse_no_patch() {
222 let version = "1.2";
223
224 let parsed = version::parse(version);
225
226 assert!(
227 parsed.is_err(),
228 "'{}' incorrectly considered a valid parse", version
229 );
230 }
231
232 #[test]
233 fn parse_empty_pre() {
234 let version = "1.2.3-";
235
236 let parsed = version::parse(version);
237
238 assert!(
239 parsed.is_err(),
240 "'{}' incorrectly considered a valid parse", version
241 );
242 }
243
244 #[test]
245 fn parse_letters() {
246 let version = "a.b.c";
247
248 let parsed = version::parse(version);
249
250 assert!(
251 parsed.is_err(),
252 "'{}' incorrectly considered a valid parse", version
253 );
254 }
255
256 #[test]
257 fn parse_with_letters() {
258 let version = "1.2.3 a.b.c";
259
260 let parsed = version::parse(version);
261
262 assert!(
263 parsed.is_err(),
264 "'{}' incorrectly considered a valid parse", version
265 );
266 }
267
268 #[test]
269 fn parse_basic_version() {
270 let version = "1.2.3";
271
272 let parsed = version::parse(version).unwrap();
273
274 assert_eq!(1, parsed.major);
275 assert_eq!(2, parsed.minor);
276 assert_eq!(3, parsed.patch);
277 }
278
279 #[test]
280 fn parse_trims_input() {
281 let version = " 1.2.3 ";
282
283 let parsed = version::parse(version).unwrap();
284
285 assert_eq!(1, parsed.major);
286 assert_eq!(2, parsed.minor);
287 assert_eq!(3, parsed.patch);
288 }
289
290 #[test]
291 fn parse_no_major_leading_zeroes() {
292 let version = "01.0.0";
293
294 let parsed = version::parse(version);
295
296 assert!(
297 parsed.is_err(),
298 "01 incorrectly considered a valid major version"
299 );
300 }
301
302 #[test]
303 fn parse_no_minor_leading_zeroes() {
304 let version = "0.01.0";
305
306 let parsed = version::parse(version);
307
308 assert!(
309 parsed.is_err(),
310 "01 incorrectly considered a valid minor version"
311 );
312 }
313
314 #[test]
315 fn parse_no_patch_leading_zeroes() {
316 let version = "0.0.01";
317
318 let parsed = version::parse(version);
319
320 assert!(
321 parsed.is_err(),
322 "01 incorrectly considered a valid patch version"
323 );
324 }
325
326 #[test]
327 fn parse_no_major_overflow() {
328 let version = "98765432109876543210.0.0";
329
330 let parsed = version::parse(version);
331
332 assert!(
333 parsed.is_err(),
334 "98765432109876543210 incorrectly considered a valid major version"
335 );
336 }
337
338 #[test]
339 fn parse_no_minor_overflow() {
340 let version = "0.98765432109876543210.0";
341
342 let parsed = version::parse(version);
343
344 assert!(
345 parsed.is_err(),
346 "98765432109876543210 incorrectly considered a valid minor version"
347 );
348 }
349
350 #[test]
351 fn parse_no_patch_overflow() {
352 let version = "0.0.98765432109876543210";
353
354 let parsed = version::parse(version);
355
356 assert!(
357 parsed.is_err(),
358 "98765432109876543210 incorrectly considered a valid patch version"
359 );
360 }
361
362 #[test]
363 fn parse_basic_prerelease() {
364 let version = "1.2.3-pre";
365
366 let parsed = version::parse(version).unwrap();
367
368 let expected_pre = vec![Identifier::AlphaNumeric(String::from("pre"))];
369 assert_eq!(expected_pre, parsed.pre);
370 }
371
372 #[test]
373 fn parse_prerelease_alphanumeric() {
374 let version = "1.2.3-alpha1";
375
376 let parsed = version::parse(version).unwrap();
377
378 let expected_pre = vec![Identifier::AlphaNumeric(String::from("alpha1"))];
379 assert_eq!(expected_pre, parsed.pre);
380 }
381
382 #[test]
383 fn parse_prerelease_zero() {
384 let version = "1.2.3-pre.0";
385
386 let parsed = version::parse(version).unwrap();
387
388 let expected_pre = vec![
389 Identifier::AlphaNumeric(String::from("pre")),
390 Identifier::Numeric(0),
391 ];
392 assert_eq!(expected_pre, parsed.pre);
393 }
394
395 #[test]
396 fn parse_basic_build() {
397 let version = "1.2.3+build";
398
399 let parsed = version::parse(version).unwrap();
400
401 let expected_build = vec![Identifier::AlphaNumeric(String::from("build"))];
402 assert_eq!(expected_build, parsed.build);
403 }
404
405 #[test]
406 fn parse_build_alphanumeric() {
407 let version = "1.2.3+build5";
408
409 let parsed = version::parse(version).unwrap();
410
411 let expected_build = vec![Identifier::AlphaNumeric(String::from("build5"))];
412 assert_eq!(expected_build, parsed.build);
413 }
414
415 #[test]
416 fn parse_pre_and_build() {
417 let version = "1.2.3-alpha1+build5";
418
419 let parsed = version::parse(version).unwrap();
420
421 let expected_pre = vec![Identifier::AlphaNumeric(String::from("alpha1"))];
422 assert_eq!(expected_pre, parsed.pre);
423
424 let expected_build = vec![Identifier::AlphaNumeric(String::from("build5"))];
425 assert_eq!(expected_build, parsed.build);
426 }
427
428 #[test]
429 fn parse_complex_metadata_01() {
430 let version = "1.2.3-1.alpha1.9+build5.7.3aedf ";
431
432 let parsed = version::parse(version).unwrap();
433
434 let expected_pre = vec![
435 Identifier::Numeric(1),
436 Identifier::AlphaNumeric(String::from("alpha1")),
437 Identifier::Numeric(9),
438 ];
439 assert_eq!(expected_pre, parsed.pre);
440
441 let expected_build = vec![
442 Identifier::AlphaNumeric(String::from("build5")),
443 Identifier::Numeric(7),
444 Identifier::AlphaNumeric(String::from("3aedf")),
445 ];
446 assert_eq!(expected_build, parsed.build);
447 }
448
449 #[test]
450 fn parse_complex_metadata_02() {
451 let version = "0.4.0-beta.1+0851523";
452
453 let parsed = version::parse(version).unwrap();
454
455 let expected_pre = vec![
456 Identifier::AlphaNumeric(String::from("beta")),
457 Identifier::Numeric(1),
458 ];
459 assert_eq!(expected_pre, parsed.pre);
460
461 let expected_build = vec![Identifier::AlphaNumeric(String::from("0851523"))];
462 assert_eq!(expected_build, parsed.build);
463 }
464
465 #[test]
466 fn parse_metadata_overflow() {
467 let version = "0.4.0-beta.1+98765432109876543210";
468
469 let parsed = version::parse(version).unwrap();
470
471 let expected_pre = vec![
472 Identifier::AlphaNumeric(String::from("beta")),
473 Identifier::Numeric(1),
474 ];
475 assert_eq!(expected_pre, parsed.pre);
476
477 let expected_build = vec![Identifier::AlphaNumeric(String::from(
478 "98765432109876543210",
479 ))];
480 assert_eq!(expected_build, parsed.build);
481 }
482
483 #[test]
484 fn parse_regression_01() {
485 let version = "0.0.0-WIP";
486
487 let parsed = version::parse(version).unwrap();
488
489 assert_eq!(0, parsed.major);
490 assert_eq!(0, parsed.minor);
491 assert_eq!(0, parsed.patch);
492
493 let expected_pre = vec![Identifier::AlphaNumeric(String::from("WIP"))];
494 assert_eq!(expected_pre, parsed.pre);
495 }
496
497 #[test]
498 fn parse_regression_02() {
499 let version = "1.2.3-beta-1";
501
502 let parsed = version::parse(version).unwrap();
503
504 assert_eq!(1, parsed.major);
505 assert_eq!(2, parsed.minor);
506 assert_eq!(3, parsed.patch);
507
508 let expected_pre = vec![Identifier::AlphaNumeric(String::from("beta-1"))];
509 assert_eq!(expected_pre, parsed.pre);
510 }
511}