1 | // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at |
3 | // http://rust-lang.org/COPYRIGHT. |
4 | // |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
8 | // option. This file may not be copied, modified, or distributed |
9 | // except according to those terms. |
10 | |
11 | //! The `version` module gives you tools to create and compare SemVer-compliant |
12 | //! versions. |
13 | |
14 | use std::cmp::{self, Ordering}; |
15 | use std::fmt; |
16 | use std::hash; |
17 | use std::error::Error; |
18 | |
19 | use std::result; |
20 | use std::str; |
21 | |
22 | use semver_parser; |
23 | |
24 | #[cfg (feature = "serde" )] |
25 | use serde::ser::{Serialize, Serializer}; |
26 | #[cfg (feature = "serde" )] |
27 | use serde::de::{self, Deserialize, Deserializer, Visitor}; |
28 | |
29 | /// An identifier in the pre-release or build metadata. |
30 | /// |
31 | /// See sections 9 and 10 of the spec for more about pre-release identifers and |
32 | /// build metadata. |
33 | #[derive (Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] |
34 | pub enum Identifier { |
35 | /// An identifier that's solely numbers. |
36 | Numeric(u64), |
37 | /// An identifier with letters and numbers. |
38 | AlphaNumeric(String), |
39 | } |
40 | |
41 | impl From<semver_parser::version::Identifier> for Identifier { |
42 | fn from(other: semver_parser::version::Identifier) -> Identifier { |
43 | match other { |
44 | semver_parser::version::Identifier::Numeric(n: u64) => Identifier::Numeric(n), |
45 | semver_parser::version::Identifier::AlphaNumeric(s: String) => Identifier::AlphaNumeric(s), |
46 | } |
47 | } |
48 | } |
49 | |
50 | impl fmt::Display for Identifier { |
51 | #[inline ] |
52 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
53 | match *self { |
54 | Identifier::Numeric(ref n: &u64) => fmt::Display::fmt(self:n, f), |
55 | Identifier::AlphaNumeric(ref s: &String) => fmt::Display::fmt(self:s, f), |
56 | } |
57 | } |
58 | } |
59 | |
60 | #[cfg (feature = "serde" )] |
61 | impl Serialize for Identifier { |
62 | fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error> |
63 | where S: Serializer |
64 | { |
65 | // Serialize Identifier as a number or string. |
66 | match *self { |
67 | Identifier::Numeric(n) => serializer.serialize_u64(n), |
68 | Identifier::AlphaNumeric(ref s) => serializer.serialize_str(s), |
69 | } |
70 | } |
71 | } |
72 | |
73 | #[cfg (feature = "serde" )] |
74 | impl<'de> Deserialize<'de> for Identifier { |
75 | fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error> |
76 | where D: Deserializer<'de> |
77 | { |
78 | struct IdentifierVisitor; |
79 | |
80 | // Deserialize Identifier from a number or string. |
81 | impl<'de> Visitor<'de> for IdentifierVisitor { |
82 | type Value = Identifier; |
83 | |
84 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
85 | formatter.write_str("a SemVer pre-release or build identifier" ) |
86 | } |
87 | |
88 | fn visit_u64<E>(self, numeric: u64) -> result::Result<Self::Value, E> |
89 | where E: de::Error |
90 | { |
91 | Ok(Identifier::Numeric(numeric)) |
92 | } |
93 | |
94 | fn visit_str<E>(self, alphanumeric: &str) -> result::Result<Self::Value, E> |
95 | where E: de::Error |
96 | { |
97 | Ok(Identifier::AlphaNumeric(alphanumeric.to_owned())) |
98 | } |
99 | } |
100 | |
101 | deserializer.deserialize_any(IdentifierVisitor) |
102 | } |
103 | } |
104 | |
105 | /// Represents a version number conforming to the semantic versioning scheme. |
106 | #[derive (Clone, Eq, Debug)] |
107 | pub struct Version { |
108 | /// The major version, to be incremented on incompatible changes. |
109 | pub major: u64, |
110 | /// The minor version, to be incremented when functionality is added in a |
111 | /// backwards-compatible manner. |
112 | pub minor: u64, |
113 | /// The patch version, to be incremented when backwards-compatible bug |
114 | /// fixes are made. |
115 | pub patch: u64, |
116 | /// The pre-release version identifier, if one exists. |
117 | pub pre: Vec<Identifier>, |
118 | /// The build metadata, ignored when determining version precedence. |
119 | pub build: Vec<Identifier>, |
120 | } |
121 | |
122 | impl From<semver_parser::version::Version> for Version { |
123 | fn from(other: semver_parser::version::Version) -> Version { |
124 | Version { |
125 | major: other.major, |
126 | minor: other.minor, |
127 | patch: other.patch, |
128 | pre: other.pre.into_iter().map(From::from).collect(), |
129 | build: other.build.into_iter().map(From::from).collect(), |
130 | } |
131 | } |
132 | } |
133 | |
134 | #[cfg (feature = "serde" )] |
135 | impl Serialize for Version { |
136 | fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error> |
137 | where S: Serializer |
138 | { |
139 | // Serialize Version as a string. |
140 | serializer.collect_str(self) |
141 | } |
142 | } |
143 | |
144 | #[cfg (feature = "serde" )] |
145 | impl<'de> Deserialize<'de> for Version { |
146 | fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error> |
147 | where D: Deserializer<'de> |
148 | { |
149 | struct VersionVisitor; |
150 | |
151 | // Deserialize Version from a string. |
152 | impl<'de> Visitor<'de> for VersionVisitor { |
153 | type Value = Version; |
154 | |
155 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
156 | formatter.write_str("a SemVer version as a string" ) |
157 | } |
158 | |
159 | fn visit_str<E>(self, v: &str) -> result::Result<Self::Value, E> |
160 | where E: de::Error |
161 | { |
162 | Version::parse(v).map_err(de::Error::custom) |
163 | } |
164 | } |
165 | |
166 | deserializer.deserialize_str(VersionVisitor) |
167 | } |
168 | } |
169 | |
170 | /// An error type for this crate |
171 | /// |
172 | /// Currently, just a generic error. Will make this nicer later. |
173 | #[derive (Clone,PartialEq,Debug,PartialOrd)] |
174 | pub enum SemVerError { |
175 | /// An error ocurred while parsing. |
176 | ParseError(String), |
177 | } |
178 | |
179 | impl fmt::Display for SemVerError { |
180 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
181 | match self { |
182 | &SemVerError::ParseError(ref m: &String) => write!(f, " {}" , m), |
183 | } |
184 | } |
185 | } |
186 | |
187 | impl Error for SemVerError { |
188 | fn description(&self) -> &str { |
189 | match self { |
190 | &SemVerError::ParseError(ref m: &String) => m, |
191 | } |
192 | } |
193 | } |
194 | |
195 | /// A Result type for errors |
196 | pub type Result<T> = result::Result<T, SemVerError>; |
197 | |
198 | impl Version { |
199 | |
200 | /// Contructs the simple case without pre or build. |
201 | pub fn new(major: u64, minor: u64, patch: u64) -> Version { |
202 | Version { |
203 | major: major, |
204 | minor: minor, |
205 | patch: patch, |
206 | pre: Vec::new(), |
207 | build: Vec::new() |
208 | } |
209 | } |
210 | |
211 | /// Parse a string into a semver object. |
212 | pub fn parse(version: &str) -> Result<Version> { |
213 | let res = semver_parser::version::parse(version); |
214 | |
215 | match res { |
216 | // Convert plain String error into proper ParseError |
217 | Err(e) => Err(SemVerError::ParseError(e)), |
218 | Ok(v) => Ok(From::from(v)), |
219 | } |
220 | } |
221 | |
222 | /// Clears the build metadata |
223 | fn clear_metadata(&mut self) { |
224 | self.build = Vec::new(); |
225 | self.pre = Vec::new(); |
226 | } |
227 | |
228 | /// Increments the patch number for this Version (Must be mutable) |
229 | pub fn increment_patch(&mut self) { |
230 | self.patch += 1; |
231 | self.clear_metadata(); |
232 | } |
233 | |
234 | /// Increments the minor version number for this Version (Must be mutable) |
235 | /// |
236 | /// As instructed by section 7 of the spec, the patch number is reset to 0. |
237 | pub fn increment_minor(&mut self) { |
238 | self.minor += 1; |
239 | self.patch = 0; |
240 | self.clear_metadata(); |
241 | } |
242 | |
243 | /// Increments the major version number for this Version (Must be mutable) |
244 | /// |
245 | /// As instructed by section 8 of the spec, the minor and patch numbers are |
246 | /// reset to 0 |
247 | pub fn increment_major(&mut self) { |
248 | self.major += 1; |
249 | self.minor = 0; |
250 | self.patch = 0; |
251 | self.clear_metadata(); |
252 | } |
253 | |
254 | /// Checks to see if the current Version is in pre-release status |
255 | pub fn is_prerelease(&self) -> bool { |
256 | !self.pre.is_empty() |
257 | } |
258 | } |
259 | |
260 | impl str::FromStr for Version { |
261 | type Err = SemVerError; |
262 | |
263 | fn from_str(s: &str) -> Result<Version> { |
264 | Version::parse(version:s) |
265 | } |
266 | } |
267 | |
268 | impl fmt::Display for Version { |
269 | #[inline ] |
270 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
271 | try!(write!(f, " {}. {}. {}" , self.major, self.minor, self.patch)); |
272 | if !self.pre.is_empty() { |
273 | try!(write!(f, "-" )); |
274 | for (i, x) in self.pre.iter().enumerate() { |
275 | if i != 0 { |
276 | try!(write!(f, "." )) |
277 | } |
278 | try!(write!(f, " {}" , x)); |
279 | } |
280 | } |
281 | if !self.build.is_empty() { |
282 | try!(write!(f, "+" )); |
283 | for (i, x) in self.build.iter().enumerate() { |
284 | if i != 0 { |
285 | try!(write!(f, "." )) |
286 | } |
287 | try!(write!(f, " {}" , x)); |
288 | } |
289 | } |
290 | Ok(()) |
291 | } |
292 | } |
293 | |
294 | impl cmp::PartialEq for Version { |
295 | #[inline ] |
296 | fn eq(&self, other: &Version) -> bool { |
297 | // We should ignore build metadata here, otherwise versions v1 and v2 |
298 | // can exist such that !(v1 < v2) && !(v1 > v2) && v1 != v2, which |
299 | // violate strict total ordering rules. |
300 | self.major == other.major && self.minor == other.minor && self.patch == other.patch && |
301 | self.pre == other.pre |
302 | } |
303 | } |
304 | |
305 | impl cmp::PartialOrd for Version { |
306 | fn partial_cmp(&self, other: &Version) -> Option<Ordering> { |
307 | Some(self.cmp(other)) |
308 | } |
309 | } |
310 | |
311 | impl cmp::Ord for Version { |
312 | fn cmp(&self, other: &Version) -> Ordering { |
313 | match self.major.cmp(&other.major) { |
314 | Ordering::Equal => {} |
315 | r => return r, |
316 | } |
317 | |
318 | match self.minor.cmp(&other.minor) { |
319 | Ordering::Equal => {} |
320 | r => return r, |
321 | } |
322 | |
323 | match self.patch.cmp(&other.patch) { |
324 | Ordering::Equal => {} |
325 | r => return r, |
326 | } |
327 | |
328 | // NB: semver spec says 0.0.0-pre < 0.0.0 |
329 | // but the version of ord defined for vec |
330 | // says that [] < [pre] so we alter it here |
331 | match (self.pre.len(), other.pre.len()) { |
332 | (0, 0) => Ordering::Equal, |
333 | (0, _) => Ordering::Greater, |
334 | (_, 0) => Ordering::Less, |
335 | (_, _) => self.pre.cmp(&other.pre), |
336 | } |
337 | } |
338 | } |
339 | |
340 | impl hash::Hash for Version { |
341 | fn hash<H: hash::Hasher>(&self, into: &mut H) { |
342 | self.major.hash(state:into); |
343 | self.minor.hash(state:into); |
344 | self.patch.hash(state:into); |
345 | self.pre.hash(state:into); |
346 | } |
347 | } |
348 | |
349 | impl From<(u64,u64,u64)> for Version { |
350 | fn from(tuple: (u64,u64,u64)) -> Version { |
351 | let (major: u64, minor: u64, patch: u64) = tuple; |
352 | Version::new(major, minor, patch) |
353 | } |
354 | } |
355 | |
356 | #[cfg (test)] |
357 | mod tests { |
358 | use std::result; |
359 | use super::Version; |
360 | use super::Identifier; |
361 | use super::SemVerError; |
362 | |
363 | #[test ] |
364 | fn test_parse() { |
365 | fn parse_error(e: &str) -> result::Result<Version, SemVerError> { |
366 | return Err(SemVerError::ParseError(e.to_string())); |
367 | } |
368 | |
369 | assert_eq!(Version::parse("" ), |
370 | parse_error("Error parsing major identifier" )); |
371 | assert_eq!(Version::parse(" " ), |
372 | parse_error("Error parsing major identifier" )); |
373 | assert_eq!(Version::parse("1" ), |
374 | parse_error("Expected dot" )); |
375 | assert_eq!(Version::parse("1.2" ), |
376 | parse_error("Expected dot" )); |
377 | assert_eq!(Version::parse("1.2.3-" ), |
378 | parse_error("Error parsing prerelease" )); |
379 | assert_eq!(Version::parse("a.b.c" ), |
380 | parse_error("Error parsing major identifier" )); |
381 | assert_eq!(Version::parse("1.2.3 abc" ), |
382 | parse_error("Extra junk after valid version: abc" )); |
383 | |
384 | assert_eq!(Version::parse("1.2.3" ), |
385 | Ok(Version { |
386 | major: 1, |
387 | minor: 2, |
388 | patch: 3, |
389 | pre: Vec::new(), |
390 | build: Vec::new(), |
391 | })); |
392 | |
393 | assert_eq!(Version::parse("1.2.3" ), |
394 | Ok(Version::new(1,2,3))); |
395 | |
396 | assert_eq!(Version::parse(" 1.2.3 " ), |
397 | Ok(Version { |
398 | major: 1, |
399 | minor: 2, |
400 | patch: 3, |
401 | pre: Vec::new(), |
402 | build: Vec::new(), |
403 | })); |
404 | assert_eq!(Version::parse("1.2.3-alpha1" ), |
405 | Ok(Version { |
406 | major: 1, |
407 | minor: 2, |
408 | patch: 3, |
409 | pre: vec![Identifier::AlphaNumeric(String::from("alpha1" ))], |
410 | build: Vec::new(), |
411 | })); |
412 | assert_eq!(Version::parse(" 1.2.3-alpha1 " ), |
413 | Ok(Version { |
414 | major: 1, |
415 | minor: 2, |
416 | patch: 3, |
417 | pre: vec![Identifier::AlphaNumeric(String::from("alpha1" ))], |
418 | build: Vec::new(), |
419 | })); |
420 | assert_eq!(Version::parse("1.2.3+build5" ), |
421 | Ok(Version { |
422 | major: 1, |
423 | minor: 2, |
424 | patch: 3, |
425 | pre: Vec::new(), |
426 | build: vec![Identifier::AlphaNumeric(String::from("build5" ))], |
427 | })); |
428 | assert_eq!(Version::parse(" 1.2.3+build5 " ), |
429 | Ok(Version { |
430 | major: 1, |
431 | minor: 2, |
432 | patch: 3, |
433 | pre: Vec::new(), |
434 | build: vec![Identifier::AlphaNumeric(String::from("build5" ))], |
435 | })); |
436 | assert_eq!(Version::parse("1.2.3-alpha1+build5" ), |
437 | Ok(Version { |
438 | major: 1, |
439 | minor: 2, |
440 | patch: 3, |
441 | pre: vec![Identifier::AlphaNumeric(String::from("alpha1" ))], |
442 | build: vec![Identifier::AlphaNumeric(String::from("build5" ))], |
443 | })); |
444 | assert_eq!(Version::parse(" 1.2.3-alpha1+build5 " ), |
445 | Ok(Version { |
446 | major: 1, |
447 | minor: 2, |
448 | patch: 3, |
449 | pre: vec![Identifier::AlphaNumeric(String::from("alpha1" ))], |
450 | build: vec![Identifier::AlphaNumeric(String::from("build5" ))], |
451 | })); |
452 | assert_eq!(Version::parse("1.2.3-1.alpha1.9+build5.7.3aedf " ), |
453 | Ok(Version { |
454 | major: 1, |
455 | minor: 2, |
456 | patch: 3, |
457 | pre: vec![Identifier::Numeric(1), |
458 | Identifier::AlphaNumeric(String::from("alpha1" )), |
459 | Identifier::Numeric(9), |
460 | ], |
461 | build: vec![Identifier::AlphaNumeric(String::from("build5" )), |
462 | Identifier::Numeric(7), |
463 | Identifier::AlphaNumeric(String::from("3aedf" )), |
464 | ], |
465 | })); |
466 | assert_eq!(Version::parse("0.4.0-beta.1+0851523" ), |
467 | Ok(Version { |
468 | major: 0, |
469 | minor: 4, |
470 | patch: 0, |
471 | pre: vec![Identifier::AlphaNumeric(String::from("beta" )), |
472 | Identifier::Numeric(1), |
473 | ], |
474 | build: vec![Identifier::AlphaNumeric(String::from("0851523" ))], |
475 | })); |
476 | |
477 | } |
478 | |
479 | #[test ] |
480 | fn test_increment_patch() { |
481 | let mut buggy_release = Version::parse("0.1.0" ).unwrap(); |
482 | buggy_release.increment_patch(); |
483 | assert_eq!(buggy_release, Version::parse("0.1.1" ).unwrap()); |
484 | } |
485 | |
486 | #[test ] |
487 | fn test_increment_minor() { |
488 | let mut feature_release = Version::parse("1.4.6" ).unwrap(); |
489 | feature_release.increment_minor(); |
490 | assert_eq!(feature_release, Version::parse("1.5.0" ).unwrap()); |
491 | } |
492 | |
493 | #[test ] |
494 | fn test_increment_major() { |
495 | let mut chrome_release = Version::parse("46.1.246773" ).unwrap(); |
496 | chrome_release.increment_major(); |
497 | assert_eq!(chrome_release, Version::parse("47.0.0" ).unwrap()); |
498 | } |
499 | |
500 | #[test ] |
501 | fn test_increment_keep_prerelease() { |
502 | let mut release = Version::parse("1.0.0-alpha" ).unwrap(); |
503 | release.increment_patch(); |
504 | |
505 | assert_eq!(release, Version::parse("1.0.1" ).unwrap()); |
506 | |
507 | release.increment_minor(); |
508 | |
509 | assert_eq!(release, Version::parse("1.1.0" ).unwrap()); |
510 | |
511 | release.increment_major(); |
512 | |
513 | assert_eq!(release, Version::parse("2.0.0" ).unwrap()); |
514 | } |
515 | |
516 | |
517 | #[test ] |
518 | fn test_increment_clear_metadata() { |
519 | let mut release = Version::parse("1.0.0+4442" ).unwrap(); |
520 | release.increment_patch(); |
521 | |
522 | assert_eq!(release, Version::parse("1.0.1" ).unwrap()); |
523 | release = Version::parse("1.0.1+hello" ).unwrap(); |
524 | |
525 | release.increment_minor(); |
526 | |
527 | assert_eq!(release, Version::parse("1.1.0" ).unwrap()); |
528 | release = Version::parse("1.1.3747+hello" ).unwrap(); |
529 | |
530 | release.increment_major(); |
531 | |
532 | assert_eq!(release, Version::parse("2.0.0" ).unwrap()); |
533 | } |
534 | |
535 | #[test ] |
536 | fn test_eq() { |
537 | assert_eq!(Version::parse("1.2.3" ), Version::parse("1.2.3" )); |
538 | assert_eq!(Version::parse("1.2.3-alpha1" ), |
539 | Version::parse("1.2.3-alpha1" )); |
540 | assert_eq!(Version::parse("1.2.3+build.42" ), |
541 | Version::parse("1.2.3+build.42" )); |
542 | assert_eq!(Version::parse("1.2.3-alpha1+42" ), |
543 | Version::parse("1.2.3-alpha1+42" )); |
544 | assert_eq!(Version::parse("1.2.3+23" ), Version::parse("1.2.3+42" )); |
545 | } |
546 | |
547 | #[test ] |
548 | fn test_ne() { |
549 | assert!(Version::parse("0.0.0" ) != Version::parse("0.0.1" )); |
550 | assert!(Version::parse("0.0.0" ) != Version::parse("0.1.0" )); |
551 | assert!(Version::parse("0.0.0" ) != Version::parse("1.0.0" )); |
552 | assert!(Version::parse("1.2.3-alpha" ) != Version::parse("1.2.3-beta" )); |
553 | } |
554 | |
555 | #[test ] |
556 | fn test_show() { |
557 | assert_eq!(format!("{}" , Version::parse("1.2.3" ).unwrap()), |
558 | "1.2.3" .to_string()); |
559 | assert_eq!(format!("{}" , Version::parse("1.2.3-alpha1" ).unwrap()), |
560 | "1.2.3-alpha1" .to_string()); |
561 | assert_eq!(format!("{}" , Version::parse("1.2.3+build.42" ).unwrap()), |
562 | "1.2.3+build.42" .to_string()); |
563 | assert_eq!(format!("{}" , Version::parse("1.2.3-alpha1+42" ).unwrap()), |
564 | "1.2.3-alpha1+42" .to_string()); |
565 | } |
566 | |
567 | #[test ] |
568 | fn test_to_string() { |
569 | assert_eq!(Version::parse("1.2.3" ).unwrap().to_string(), |
570 | "1.2.3" .to_string()); |
571 | assert_eq!(Version::parse("1.2.3-alpha1" ).unwrap().to_string(), |
572 | "1.2.3-alpha1" .to_string()); |
573 | assert_eq!(Version::parse("1.2.3+build.42" ).unwrap().to_string(), |
574 | "1.2.3+build.42" .to_string()); |
575 | assert_eq!(Version::parse("1.2.3-alpha1+42" ).unwrap().to_string(), |
576 | "1.2.3-alpha1+42" .to_string()); |
577 | } |
578 | |
579 | #[test ] |
580 | fn test_lt() { |
581 | assert!(Version::parse("0.0.0" ) < Version::parse("1.2.3-alpha2" )); |
582 | assert!(Version::parse("1.0.0" ) < Version::parse("1.2.3-alpha2" )); |
583 | assert!(Version::parse("1.2.0" ) < Version::parse("1.2.3-alpha2" )); |
584 | assert!(Version::parse("1.2.3-alpha1" ) < Version::parse("1.2.3" )); |
585 | assert!(Version::parse("1.2.3-alpha1" ) < Version::parse("1.2.3-alpha2" )); |
586 | assert!(!(Version::parse("1.2.3-alpha2" ) < Version::parse("1.2.3-alpha2" ))); |
587 | assert!(!(Version::parse("1.2.3+23" ) < Version::parse("1.2.3+42" ))); |
588 | } |
589 | |
590 | #[test ] |
591 | fn test_le() { |
592 | assert!(Version::parse("0.0.0" ) <= Version::parse("1.2.3-alpha2" )); |
593 | assert!(Version::parse("1.0.0" ) <= Version::parse("1.2.3-alpha2" )); |
594 | assert!(Version::parse("1.2.0" ) <= Version::parse("1.2.3-alpha2" )); |
595 | assert!(Version::parse("1.2.3-alpha1" ) <= Version::parse("1.2.3-alpha2" )); |
596 | assert!(Version::parse("1.2.3-alpha2" ) <= Version::parse("1.2.3-alpha2" )); |
597 | assert!(Version::parse("1.2.3+23" ) <= Version::parse("1.2.3+42" )); |
598 | } |
599 | |
600 | #[test ] |
601 | fn test_gt() { |
602 | assert!(Version::parse("1.2.3-alpha2" ) > Version::parse("0.0.0" )); |
603 | assert!(Version::parse("1.2.3-alpha2" ) > Version::parse("1.0.0" )); |
604 | assert!(Version::parse("1.2.3-alpha2" ) > Version::parse("1.2.0" )); |
605 | assert!(Version::parse("1.2.3-alpha2" ) > Version::parse("1.2.3-alpha1" )); |
606 | assert!(Version::parse("1.2.3" ) > Version::parse("1.2.3-alpha2" )); |
607 | assert!(!(Version::parse("1.2.3-alpha2" ) > Version::parse("1.2.3-alpha2" ))); |
608 | assert!(!(Version::parse("1.2.3+23" ) > Version::parse("1.2.3+42" ))); |
609 | } |
610 | |
611 | #[test ] |
612 | fn test_ge() { |
613 | assert!(Version::parse("1.2.3-alpha2" ) >= Version::parse("0.0.0" )); |
614 | assert!(Version::parse("1.2.3-alpha2" ) >= Version::parse("1.0.0" )); |
615 | assert!(Version::parse("1.2.3-alpha2" ) >= Version::parse("1.2.0" )); |
616 | assert!(Version::parse("1.2.3-alpha2" ) >= Version::parse("1.2.3-alpha1" )); |
617 | assert!(Version::parse("1.2.3-alpha2" ) >= Version::parse("1.2.3-alpha2" )); |
618 | assert!(Version::parse("1.2.3+23" ) >= Version::parse("1.2.3+42" )); |
619 | } |
620 | |
621 | #[test ] |
622 | fn test_prerelease_check() { |
623 | assert!(Version::parse("1.0.0" ).unwrap().is_prerelease() == false); |
624 | assert!(Version::parse("0.0.1" ).unwrap().is_prerelease() == false); |
625 | assert!(Version::parse("4.1.4-alpha" ).unwrap().is_prerelease()); |
626 | assert!(Version::parse("1.0.0-beta294296" ).unwrap().is_prerelease()); |
627 | } |
628 | |
629 | #[test ] |
630 | fn test_spec_order() { |
631 | let vs = ["1.0.0-alpha" , |
632 | "1.0.0-alpha.1" , |
633 | "1.0.0-alpha.beta" , |
634 | "1.0.0-beta" , |
635 | "1.0.0-beta.2" , |
636 | "1.0.0-beta.11" , |
637 | "1.0.0-rc.1" , |
638 | "1.0.0" ]; |
639 | let mut i = 1; |
640 | while i < vs.len() { |
641 | let a = Version::parse(vs[i - 1]); |
642 | let b = Version::parse(vs[i]); |
643 | assert!(a < b, "nope {:?} < {:?}" , a, b); |
644 | i += 1; |
645 | } |
646 | } |
647 | |
648 | #[test ] |
649 | fn test_from_str() { |
650 | assert_eq!("1.2.3" .parse(), |
651 | Ok(Version { |
652 | major: 1, |
653 | minor: 2, |
654 | patch: 3, |
655 | pre: Vec::new(), |
656 | build: Vec::new(), |
657 | })); |
658 | assert_eq!(" 1.2.3 " .parse(), |
659 | Ok(Version { |
660 | major: 1, |
661 | minor: 2, |
662 | patch: 3, |
663 | pre: Vec::new(), |
664 | build: Vec::new(), |
665 | })); |
666 | assert_eq!("1.2.3-alpha1" .parse(), |
667 | Ok(Version { |
668 | major: 1, |
669 | minor: 2, |
670 | patch: 3, |
671 | pre: vec![Identifier::AlphaNumeric(String::from("alpha1" ))], |
672 | build: Vec::new(), |
673 | })); |
674 | assert_eq!(" 1.2.3-alpha1 " .parse(), |
675 | Ok(Version { |
676 | major: 1, |
677 | minor: 2, |
678 | patch: 3, |
679 | pre: vec![Identifier::AlphaNumeric(String::from("alpha1" ))], |
680 | build: Vec::new(), |
681 | })); |
682 | assert_eq!("1.2.3+build5" .parse(), |
683 | Ok(Version { |
684 | major: 1, |
685 | minor: 2, |
686 | patch: 3, |
687 | pre: Vec::new(), |
688 | build: vec![Identifier::AlphaNumeric(String::from("build5" ))], |
689 | })); |
690 | assert_eq!(" 1.2.3+build5 " .parse(), |
691 | Ok(Version { |
692 | major: 1, |
693 | minor: 2, |
694 | patch: 3, |
695 | pre: Vec::new(), |
696 | build: vec![Identifier::AlphaNumeric(String::from("build5" ))], |
697 | })); |
698 | assert_eq!("1.2.3-alpha1+build5" .parse(), |
699 | Ok(Version { |
700 | major: 1, |
701 | minor: 2, |
702 | patch: 3, |
703 | pre: vec![Identifier::AlphaNumeric(String::from("alpha1" ))], |
704 | build: vec![Identifier::AlphaNumeric(String::from("build5" ))], |
705 | })); |
706 | assert_eq!(" 1.2.3-alpha1+build5 " .parse(), |
707 | Ok(Version { |
708 | major: 1, |
709 | minor: 2, |
710 | patch: 3, |
711 | pre: vec![Identifier::AlphaNumeric(String::from("alpha1" ))], |
712 | build: vec![Identifier::AlphaNumeric(String::from("build5" ))], |
713 | })); |
714 | assert_eq!("1.2.3-1.alpha1.9+build5.7.3aedf " .parse(), |
715 | Ok(Version { |
716 | major: 1, |
717 | minor: 2, |
718 | patch: 3, |
719 | pre: vec![Identifier::Numeric(1), |
720 | Identifier::AlphaNumeric(String::from("alpha1" )), |
721 | Identifier::Numeric(9), |
722 | ], |
723 | build: vec![Identifier::AlphaNumeric(String::from("build5" )), |
724 | Identifier::Numeric(7), |
725 | Identifier::AlphaNumeric(String::from("3aedf" )), |
726 | ], |
727 | })); |
728 | assert_eq!("0.4.0-beta.1+0851523" .parse(), |
729 | Ok(Version { |
730 | major: 0, |
731 | minor: 4, |
732 | patch: 0, |
733 | pre: vec![Identifier::AlphaNumeric(String::from("beta" )), |
734 | Identifier::Numeric(1), |
735 | ], |
736 | build: vec![Identifier::AlphaNumeric(String::from("0851523" ))], |
737 | })); |
738 | |
739 | } |
740 | |
741 | #[test ] |
742 | fn test_from_str_errors() { |
743 | fn parse_error(e: &str) -> result::Result<Version, SemVerError> { |
744 | return Err(SemVerError::ParseError(e.to_string())); |
745 | } |
746 | |
747 | assert_eq!("" .parse(), parse_error("Error parsing major identifier" )); |
748 | assert_eq!(" " .parse(), parse_error("Error parsing major identifier" )); |
749 | assert_eq!("1" .parse(), parse_error("Expected dot" )); |
750 | assert_eq!("1.2" .parse(), |
751 | parse_error("Expected dot" )); |
752 | assert_eq!("1.2.3-" .parse(), |
753 | parse_error("Error parsing prerelease" )); |
754 | assert_eq!("a.b.c" .parse(), |
755 | parse_error("Error parsing major identifier" )); |
756 | assert_eq!("1.2.3 abc" .parse(), |
757 | parse_error("Extra junk after valid version: abc" )); |
758 | } |
759 | } |
760 | |