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.11-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};
45 use std::strbuf::StrBuf;
47 /// An identifier in the pre-release or build metadata. If the identifier can
48 /// be parsed as a decimal value, it will be represented with `Numeric`.
49 #[deriving(Clone, Eq)]
56 impl cmp::Ord for Identifier {
58 fn lt(&self, other: &Identifier) -> bool {
60 (&Numeric(a), &Numeric(b)) => a < b,
61 (&Numeric(_), _) => true,
62 (&AlphaNumeric(ref a), &AlphaNumeric(ref b)) => *a < *b,
63 (&AlphaNumeric(_), _) => false
68 impl fmt::Show for Identifier {
70 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72 Numeric(ref n) => n.fmt(f),
73 AlphaNumeric(ref s) => s.fmt(f)
79 /// Represents a version number conforming to the semantic versioning scheme.
82 /// The major version, to be incremented on incompatible changes.
84 /// The minor version, to be incremented when functionality is added in a
85 /// backwards-compatible manner.
87 /// The patch version, to be incremented when backwards-compatible bug
90 /// The pre-release version identifier, if one exists.
91 pub pre: Vec<Identifier>,
92 /// The build metadata, ignored when determining version precedence.
93 pub build: Vec<Identifier>,
96 impl fmt::Show for Version {
98 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99 try!(write!(f.buf, "{}.{}.{}", self.major, self.minor, self.patch))
100 if !self.pre.is_empty() {
101 try!(write!(f.buf, "-"));
102 for (i, x) in self.pre.iter().enumerate() {
103 if i != 0 { try!(write!(f.buf, ".")) };
107 if !self.build.is_empty() {
108 try!(write!(f.buf, "+"));
109 for (i, x) in self.build.iter().enumerate() {
110 if i != 0 { try!(write!(f.buf, ".")) };
118 impl cmp::Eq for Version {
120 fn eq(&self, other: &Version) -> bool {
121 // We should ignore build metadata here, otherwise versions v1 and v2
122 // can exist such that !(v1 < v2) && !(v1 > v2) && v1 != v2, which
123 // violate strict total ordering rules.
124 self.major == other.major &&
125 self.minor == other.minor &&
126 self.patch == other.patch &&
127 self.pre == other.pre
131 impl cmp::Ord for Version {
133 fn lt(&self, other: &Version) -> bool {
135 self.major < other.major ||
137 (self.major == other.major &&
138 self.minor < other.minor) ||
140 (self.major == other.major &&
141 self.minor == other.minor &&
142 self.patch < other.patch) ||
144 (self.major == other.major &&
145 self.minor == other.minor &&
146 self.patch == other.patch &&
147 // NB: semver spec says 0.0.0-pre < 0.0.0
148 // but the version of ord defined for vec
149 // says that [] < [pre], so we alter it
151 (match (self.pre.len(), other.pre.len()) {
155 (_, _) => self.pre < other.pre
160 fn take_nonempty_prefix<T:Iterator<char>>(rdr: &mut T, pred: |char| -> bool)
161 -> (~str, Option<char>) {
162 let mut buf = StrBuf::new();
163 let mut ch = rdr.next();
167 Some(c) if !pred(c) => break,
174 (buf.into_owned(), ch)
177 fn take_num<T: Iterator<char>>(rdr: &mut T) -> Option<(uint, Option<char>)> {
178 let (s, ch) = take_nonempty_prefix(rdr, char::is_digit);
179 match from_str::<uint>(s) {
181 Some(i) => Some((i, ch))
185 fn take_ident<T: Iterator<char>>(rdr: &mut T) -> Option<(Identifier, Option<char>)> {
186 let (s,ch) = take_nonempty_prefix(rdr, char::is_alphanumeric);
187 if s.chars().all(char::is_digit) {
188 match from_str::<uint>(s) {
190 Some(i) => Some((Numeric(i), ch))
193 Some((AlphaNumeric(s), ch))
197 fn expect(ch: Option<char>, c: char) -> Option<()> {
205 fn parse_iter<T: Iterator<char>>(rdr: &mut T) -> Option<Version> {
206 let maybe_vers = take_num(rdr).and_then(|(major, ch)| {
207 expect(ch, '.').and_then(|_| Some(major))
208 }).and_then(|major| {
209 take_num(rdr).and_then(|(minor, ch)| {
210 expect(ch, '.').and_then(|_| Some((major, minor)))
212 }).and_then(|(major, minor)| {
213 take_num(rdr).and_then(|(patch, ch)| {
214 Some((major, minor, patch, ch))
218 let (major, minor, patch, ch) = match maybe_vers {
219 Some((a, b, c, d)) => (a, b, c, d),
223 let mut pre = vec!();
224 let mut build = vec!();
229 let (id, c) = match take_ident(rdr) {
230 Some((id, c)) => (id, c),
235 if ch != Some('.') { break; }
241 let (id, c) = match take_ident(rdr) {
242 Some((id, c)) => (id, c),
247 if ch != Some('.') { break; }
261 /// Parse a string into a semver object.
262 pub fn parse(s: &str) -> Option<Version> {
267 let v = parse_iter(&mut s.chars());
270 if v.to_str().equiv(&s) {
282 assert_eq!(parse(""), None);
283 assert_eq!(parse(" "), None);
284 assert_eq!(parse("1"), None);
285 assert_eq!(parse("1.2"), None);
286 assert_eq!(parse("1.2"), None);
287 assert_eq!(parse("1"), None);
288 assert_eq!(parse("1.2"), None);
289 assert_eq!(parse("1.2.3-"), None);
290 assert_eq!(parse("a.b.c"), None);
291 assert_eq!(parse("1.2.3 abc"), None);
293 assert!(parse("1.2.3") == Some(Version {
300 assert!(parse(" 1.2.3 ") == Some(Version {
307 assert!(parse("1.2.3-alpha1") == Some(Version {
311 pre: vec!(AlphaNumeric(~"alpha1")),
314 assert!(parse(" 1.2.3-alpha1 ") == Some(Version {
318 pre: vec!(AlphaNumeric(~"alpha1")),
321 assert!(parse("1.2.3+build5") == Some(Version {
326 build: vec!(AlphaNumeric(~"build5"))
328 assert!(parse(" 1.2.3+build5 ") == Some(Version {
333 build: vec!(AlphaNumeric(~"build5"))
335 assert!(parse("1.2.3-alpha1+build5") == Some(Version {
339 pre: vec!(AlphaNumeric(~"alpha1")),
340 build: vec!(AlphaNumeric(~"build5"))
342 assert!(parse(" 1.2.3-alpha1+build5 ") == Some(Version {
346 pre: vec!(AlphaNumeric(~"alpha1")),
347 build: vec!(AlphaNumeric(~"build5"))
349 assert!(parse("1.2.3-1.alpha1.9+build5.7.3aedf ") == Some(Version {
353 pre: vec!(Numeric(1),AlphaNumeric(~"alpha1"),Numeric(9)),
354 build: vec!(AlphaNumeric(~"build5"),
356 AlphaNumeric(~"3aedf"))
363 assert_eq!(parse("1.2.3"), parse("1.2.3"));
364 assert_eq!(parse("1.2.3-alpha1"), parse("1.2.3-alpha1"));
365 assert_eq!(parse("1.2.3+build.42"), parse("1.2.3+build.42"));
366 assert_eq!(parse("1.2.3-alpha1+42"), parse("1.2.3-alpha1+42"));
367 assert_eq!(parse("1.2.3+23"), parse("1.2.3+42"));
372 assert!(parse("0.0.0") != parse("0.0.1"));
373 assert!(parse("0.0.0") != parse("0.1.0"));
374 assert!(parse("0.0.0") != parse("1.0.0"));
375 assert!(parse("1.2.3-alpha") != parse("1.2.3-beta"));
380 assert_eq!(format!("{}", parse("1.2.3").unwrap()), ~"1.2.3");
381 assert_eq!(format!("{}", parse("1.2.3-alpha1").unwrap()), ~"1.2.3-alpha1");
382 assert_eq!(format!("{}", parse("1.2.3+build.42").unwrap()), ~"1.2.3+build.42");
383 assert_eq!(format!("{}", parse("1.2.3-alpha1+42").unwrap()), ~"1.2.3-alpha1+42");
388 assert_eq!(parse("1.2.3").unwrap().to_str(), ~"1.2.3");
389 assert_eq!(parse("1.2.3-alpha1").unwrap().to_str(), ~"1.2.3-alpha1");
390 assert_eq!(parse("1.2.3+build.42").unwrap().to_str(), ~"1.2.3+build.42");
391 assert_eq!(parse("1.2.3-alpha1+42").unwrap().to_str(), ~"1.2.3-alpha1+42");
396 assert!(parse("0.0.0") < parse("1.2.3-alpha2"));
397 assert!(parse("1.0.0") < parse("1.2.3-alpha2"));
398 assert!(parse("1.2.0") < parse("1.2.3-alpha2"));
399 assert!(parse("1.2.3-alpha1") < parse("1.2.3"));
400 assert!(parse("1.2.3-alpha1") < parse("1.2.3-alpha2"));
401 assert!(!(parse("1.2.3-alpha2") < parse("1.2.3-alpha2")));
402 assert!(!(parse("1.2.3+23") < parse("1.2.3+42")));
407 assert!(parse("0.0.0") <= parse("1.2.3-alpha2"));
408 assert!(parse("1.0.0") <= parse("1.2.3-alpha2"));
409 assert!(parse("1.2.0") <= parse("1.2.3-alpha2"));
410 assert!(parse("1.2.3-alpha1") <= parse("1.2.3-alpha2"));
411 assert!(parse("1.2.3-alpha2") <= parse("1.2.3-alpha2"));
412 assert!(parse("1.2.3+23") <= parse("1.2.3+42"));
417 assert!(parse("1.2.3-alpha2") > parse("0.0.0"));
418 assert!(parse("1.2.3-alpha2") > parse("1.0.0"));
419 assert!(parse("1.2.3-alpha2") > parse("1.2.0"));
420 assert!(parse("1.2.3-alpha2") > parse("1.2.3-alpha1"));
421 assert!(parse("1.2.3") > parse("1.2.3-alpha2"));
422 assert!(!(parse("1.2.3-alpha2") > parse("1.2.3-alpha2")));
423 assert!(!(parse("1.2.3+23") > parse("1.2.3+42")));
428 assert!(parse("1.2.3-alpha2") >= parse("0.0.0"));
429 assert!(parse("1.2.3-alpha2") >= parse("1.0.0"));
430 assert!(parse("1.2.3-alpha2") >= parse("1.2.0"));
431 assert!(parse("1.2.3-alpha2") >= parse("1.2.3-alpha1"));
432 assert!(parse("1.2.3-alpha2") >= parse("1.2.3-alpha2"));
433 assert!(parse("1.2.3+23") >= parse("1.2.3+42"));
437 fn test_spec_order() {
438 let vs = ["1.0.0-alpha",
448 let a = parse(vs[i-1]).unwrap();
449 let b = parse(vs[i]).unwrap();