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.
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.
11 //! Semantic version parsing and comparison.
13 //! Semantic versioning (see http://semver.org/) is a set of rules for
14 //! assigning version numbers intended to convey meaning about what has
15 //! changed, and how much. A version number has five parts:
17 //! * Major number, updated for incompatible API changes
18 //! * Minor number, updated for backwards-compatible API additions
19 //! * Patch number, updated for backwards-compatible bugfixes
20 //! * Pre-release information (optional), preceded by a hyphen (`-`)
21 //! * Build metadata (optional), preceded by a plus sign (`+`)
23 //! The three mandatory components are required to be decimal numbers. The
24 //! pre-release information and build metadata are required to be a
25 //! period-separated list of identifiers containing only alphanumeric
26 //! characters and hyphens.
28 //! An example version number with all five components is
29 //! `0.8.1-rc.3.0+20130922.linux`.
31 #![crate_id = "semver#0.10-pre"]
32 #![crate_type = "rlib"]
33 #![crate_type = "dylib"]
34 #![license = "MIT/ASL2"]
35 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
36 html_favicon_url = "http://www.rust-lang.org/favicon.ico",
37 html_root_url = "http://static.rust-lang.org/doc/master")]
38 #![deny(deprecated_owned_vector)]
44 use std::option::{Option, Some, None};
46 /// An identifier in the pre-release or build metadata. If the identifier can
47 /// be parsed as a decimal value, it will be represented with `Numeric`.
48 #[deriving(Clone, Eq)]
55 impl cmp::Ord for Identifier {
57 fn lt(&self, other: &Identifier) -> bool {
59 (&Numeric(a), &Numeric(b)) => a < b,
60 (&Numeric(_), _) => true,
61 (&AlphaNumeric(ref a), &AlphaNumeric(ref b)) => *a < *b,
62 (&AlphaNumeric(_), _) => false
67 impl fmt::Show for Identifier {
69 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71 Numeric(ref n) => n.fmt(f),
72 AlphaNumeric(ref s) => s.fmt(f)
78 /// Represents a version number conforming to the semantic versioning scheme.
81 /// The major version, to be incremented on incompatible changes.
83 /// The minor version, to be incremented when functionality is added in a
84 /// backwards-compatible manner.
86 /// The patch version, to be incremented when backwards-compatible bug
89 /// The pre-release version identifier, if one exists.
91 /// The build metadata, ignored when determining version precedence.
92 build: Vec<Identifier>,
95 impl fmt::Show for Version {
97 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98 try!(write!(f.buf, "{}.{}.{}", self.major, self.minor, self.patch))
99 if !self.pre.is_empty() {
100 try!(write!(f.buf, "-"));
101 for (i, x) in self.pre.iter().enumerate() {
102 if i != 0 { try!(write!(f.buf, ".")) };
106 if !self.build.is_empty() {
107 try!(write!(f.buf, "+"));
108 for (i, x) in self.build.iter().enumerate() {
109 if i != 0 { try!(write!(f.buf, ".")) };
117 impl cmp::Eq for Version {
119 fn eq(&self, other: &Version) -> bool {
120 // We should ignore build metadata here, otherwise versions v1 and v2
121 // can exist such that !(v1 < v2) && !(v1 > v2) && v1 != v2, which
122 // violate strict total ordering rules.
123 self.major == other.major &&
124 self.minor == other.minor &&
125 self.patch == other.patch &&
126 self.pre == other.pre
130 impl cmp::Ord for Version {
132 fn lt(&self, other: &Version) -> bool {
134 self.major < other.major ||
136 (self.major == other.major &&
137 self.minor < other.minor) ||
139 (self.major == other.major &&
140 self.minor == other.minor &&
141 self.patch < other.patch) ||
143 (self.major == other.major &&
144 self.minor == other.minor &&
145 self.patch == other.patch &&
146 // NB: semver spec says 0.0.0-pre < 0.0.0
147 // but the version of ord defined for vec
148 // says that [] < [pre], so we alter it
150 (match (self.pre.len(), other.pre.len()) {
154 (_, _) => self.pre < other.pre
159 fn take_nonempty_prefix<T:Iterator<char>>(rdr: &mut T, pred: |char| -> bool)
160 -> (~str, Option<char>) {
162 let mut ch = rdr.next();
166 Some(c) if !pred(c) => break,
176 fn take_num<T: Iterator<char>>(rdr: &mut T) -> Option<(uint, Option<char>)> {
177 let (s, ch) = take_nonempty_prefix(rdr, char::is_digit);
178 match from_str::<uint>(s) {
180 Some(i) => Some((i, ch))
184 fn take_ident<T: Iterator<char>>(rdr: &mut T) -> Option<(Identifier, Option<char>)> {
185 let (s,ch) = take_nonempty_prefix(rdr, char::is_alphanumeric);
186 if s.chars().all(char::is_digit) {
187 match from_str::<uint>(s) {
189 Some(i) => Some((Numeric(i), ch))
192 Some((AlphaNumeric(s), ch))
196 fn expect(ch: Option<char>, c: char) -> Option<()> {
204 fn parse_iter<T: Iterator<char>>(rdr: &mut T) -> Option<Version> {
205 let maybe_vers = take_num(rdr).and_then(|(major, ch)| {
206 expect(ch, '.').and_then(|_| Some(major))
207 }).and_then(|major| {
208 take_num(rdr).and_then(|(minor, ch)| {
209 expect(ch, '.').and_then(|_| Some((major, minor)))
211 }).and_then(|(major, minor)| {
212 take_num(rdr).and_then(|(patch, ch)| {
213 Some((major, minor, patch, ch))
217 let (major, minor, patch, ch) = match maybe_vers {
218 Some((a, b, c, d)) => (a, b, c, d),
222 let mut pre = vec!();
223 let mut build = vec!();
228 let (id, c) = match take_ident(rdr) {
229 Some((id, c)) => (id, c),
234 if ch != Some('.') { break; }
240 let (id, c) = match take_ident(rdr) {
241 Some((id, c)) => (id, c),
246 if ch != Some('.') { break; }
260 /// Parse a string into a semver object.
261 pub fn parse(s: &str) -> Option<Version> {
266 let v = parse_iter(&mut s.chars());
269 if v.to_str().equiv(&s) {
281 assert_eq!(parse(""), None);
282 assert_eq!(parse(" "), None);
283 assert_eq!(parse("1"), None);
284 assert_eq!(parse("1.2"), None);
285 assert_eq!(parse("1.2"), None);
286 assert_eq!(parse("1"), None);
287 assert_eq!(parse("1.2"), None);
288 assert_eq!(parse("1.2.3-"), None);
289 assert_eq!(parse("a.b.c"), None);
290 assert_eq!(parse("1.2.3 abc"), None);
292 assert!(parse("1.2.3") == Some(Version {
299 assert!(parse(" 1.2.3 ") == Some(Version {
306 assert!(parse("1.2.3-alpha1") == Some(Version {
310 pre: vec!(AlphaNumeric(~"alpha1")),
313 assert!(parse(" 1.2.3-alpha1 ") == Some(Version {
317 pre: vec!(AlphaNumeric(~"alpha1")),
320 assert!(parse("1.2.3+build5") == Some(Version {
325 build: vec!(AlphaNumeric(~"build5"))
327 assert!(parse(" 1.2.3+build5 ") == Some(Version {
332 build: vec!(AlphaNumeric(~"build5"))
334 assert!(parse("1.2.3-alpha1+build5") == Some(Version {
338 pre: vec!(AlphaNumeric(~"alpha1")),
339 build: vec!(AlphaNumeric(~"build5"))
341 assert!(parse(" 1.2.3-alpha1+build5 ") == Some(Version {
345 pre: vec!(AlphaNumeric(~"alpha1")),
346 build: vec!(AlphaNumeric(~"build5"))
348 assert!(parse("1.2.3-1.alpha1.9+build5.7.3aedf ") == Some(Version {
352 pre: vec!(Numeric(1),AlphaNumeric(~"alpha1"),Numeric(9)),
353 build: vec!(AlphaNumeric(~"build5"),
355 AlphaNumeric(~"3aedf"))
362 assert_eq!(parse("1.2.3"), parse("1.2.3"));
363 assert_eq!(parse("1.2.3-alpha1"), parse("1.2.3-alpha1"));
364 assert_eq!(parse("1.2.3+build.42"), parse("1.2.3+build.42"));
365 assert_eq!(parse("1.2.3-alpha1+42"), parse("1.2.3-alpha1+42"));
366 assert_eq!(parse("1.2.3+23"), parse("1.2.3+42"));
371 assert!(parse("0.0.0") != parse("0.0.1"));
372 assert!(parse("0.0.0") != parse("0.1.0"));
373 assert!(parse("0.0.0") != parse("1.0.0"));
374 assert!(parse("1.2.3-alpha") != parse("1.2.3-beta"));
379 assert_eq!(format!("{}", parse("1.2.3").unwrap()), ~"1.2.3");
380 assert_eq!(format!("{}", parse("1.2.3-alpha1").unwrap()), ~"1.2.3-alpha1");
381 assert_eq!(format!("{}", parse("1.2.3+build.42").unwrap()), ~"1.2.3+build.42");
382 assert_eq!(format!("{}", parse("1.2.3-alpha1+42").unwrap()), ~"1.2.3-alpha1+42");
387 assert_eq!(parse("1.2.3").unwrap().to_str(), ~"1.2.3");
388 assert_eq!(parse("1.2.3-alpha1").unwrap().to_str(), ~"1.2.3-alpha1");
389 assert_eq!(parse("1.2.3+build.42").unwrap().to_str(), ~"1.2.3+build.42");
390 assert_eq!(parse("1.2.3-alpha1+42").unwrap().to_str(), ~"1.2.3-alpha1+42");
395 assert!(parse("0.0.0") < parse("1.2.3-alpha2"));
396 assert!(parse("1.0.0") < parse("1.2.3-alpha2"));
397 assert!(parse("1.2.0") < parse("1.2.3-alpha2"));
398 assert!(parse("1.2.3-alpha1") < parse("1.2.3"));
399 assert!(parse("1.2.3-alpha1") < parse("1.2.3-alpha2"));
400 assert!(!(parse("1.2.3-alpha2") < parse("1.2.3-alpha2")));
401 assert!(!(parse("1.2.3+23") < parse("1.2.3+42")));
406 assert!(parse("0.0.0") <= parse("1.2.3-alpha2"));
407 assert!(parse("1.0.0") <= parse("1.2.3-alpha2"));
408 assert!(parse("1.2.0") <= parse("1.2.3-alpha2"));
409 assert!(parse("1.2.3-alpha1") <= parse("1.2.3-alpha2"));
410 assert!(parse("1.2.3-alpha2") <= parse("1.2.3-alpha2"));
411 assert!(parse("1.2.3+23") <= parse("1.2.3+42"));
416 assert!(parse("1.2.3-alpha2") > parse("0.0.0"));
417 assert!(parse("1.2.3-alpha2") > parse("1.0.0"));
418 assert!(parse("1.2.3-alpha2") > parse("1.2.0"));
419 assert!(parse("1.2.3-alpha2") > parse("1.2.3-alpha1"));
420 assert!(parse("1.2.3") > parse("1.2.3-alpha2"));
421 assert!(!(parse("1.2.3-alpha2") > parse("1.2.3-alpha2")));
422 assert!(!(parse("1.2.3+23") > parse("1.2.3+42")));
427 assert!(parse("1.2.3-alpha2") >= parse("0.0.0"));
428 assert!(parse("1.2.3-alpha2") >= parse("1.0.0"));
429 assert!(parse("1.2.3-alpha2") >= parse("1.2.0"));
430 assert!(parse("1.2.3-alpha2") >= parse("1.2.3-alpha1"));
431 assert!(parse("1.2.3-alpha2") >= parse("1.2.3-alpha2"));
432 assert!(parse("1.2.3+23") >= parse("1.2.3+42"));
436 fn test_spec_order() {
437 let vs = ["1.0.0-alpha",
447 let a = parse(vs[i-1]).unwrap();
448 let b = parse(vs[i]).unwrap();