]> git.lizzy.rs Git - rust.git/blob - src/libsemver/lib.rs
auto merge of #13600 : brandonw/rust/master, r=brson
[rust.git] / src / libsemver / lib.rs
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 //! Semantic version parsing and comparison.
12 //!
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:
16 //!
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 (`+`)
22 //!
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.
27 //!
28 //! An example version number with all five components is
29 //! `0.8.1-rc.3.0+20130922.linux`.
30
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)]
39
40 use std::char;
41 use std::cmp;
42 use std::fmt;
43 use std::fmt::Show;
44 use std::option::{Option, Some, None};
45 use std::strbuf::StrBuf;
46
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)]
50 #[allow(missing_doc)]
51 pub enum Identifier {
52     Numeric(uint),
53     AlphaNumeric(~str)
54 }
55
56 impl cmp::Ord for Identifier {
57     #[inline]
58     fn lt(&self, other: &Identifier) -> bool {
59         match (self, other) {
60             (&Numeric(a), &Numeric(b)) => a < b,
61             (&Numeric(_), _) => true,
62             (&AlphaNumeric(ref a), &AlphaNumeric(ref b)) => *a < *b,
63             (&AlphaNumeric(_), _) => false
64         }
65     }
66 }
67
68 impl fmt::Show for Identifier {
69     #[inline]
70     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71         match *self {
72             Numeric(ref n) => n.fmt(f),
73             AlphaNumeric(ref s) => s.fmt(f)
74         }
75     }
76 }
77
78
79 /// Represents a version number conforming to the semantic versioning scheme.
80 #[deriving(Clone)]
81 pub struct Version {
82     /// The major version, to be incremented on incompatible changes.
83     pub major: uint,
84     /// The minor version, to be incremented when functionality is added in a
85     /// backwards-compatible manner.
86     pub minor: uint,
87     /// The patch version, to be incremented when backwards-compatible bug
88     /// fixes are made.
89     pub patch: uint,
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>,
94 }
95
96 impl fmt::Show for Version {
97     #[inline]
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, ".")) };
104                 try!(x.fmt(f));
105             }
106         }
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, ".")) };
111                 try!(x.fmt(f));
112             }
113         }
114         Ok(())
115     }
116 }
117
118 impl cmp::Eq for Version {
119     #[inline]
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
128     }
129 }
130
131 impl cmp::Ord for Version {
132     #[inline]
133     fn lt(&self, other: &Version) -> bool {
134
135         self.major < other.major ||
136
137             (self.major == other.major &&
138              self.minor < other.minor) ||
139
140             (self.major == other.major &&
141              self.minor == other.minor &&
142              self.patch < other.patch) ||
143
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
150              // here.
151              (match (self.pre.len(), other.pre.len()) {
152                  (0, 0) => false,
153                  (0, _) => false,
154                  (_, 0) => true,
155                  (_, _) => self.pre < other.pre
156              }))
157     }
158 }
159
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();
164     loop {
165         match ch {
166             None => break,
167             Some(c) if !pred(c) => break,
168             Some(c) => {
169                 buf.push_char(c);
170                 ch = rdr.next();
171             }
172         }
173     }
174     (buf.into_owned(), ch)
175 }
176
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) {
180         None => None,
181         Some(i) => Some((i, ch))
182     }
183 }
184
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) {
189             None => None,
190             Some(i) => Some((Numeric(i), ch))
191         }
192     } else {
193         Some((AlphaNumeric(s), ch))
194     }
195 }
196
197 fn expect(ch: Option<char>, c: char) -> Option<()> {
198     if ch != Some(c) {
199         None
200     } else {
201         Some(())
202     }
203 }
204
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)))
211         })
212     }).and_then(|(major, minor)| {
213         take_num(rdr).and_then(|(patch, ch)| {
214            Some((major, minor, patch, ch))
215         })
216     });
217
218     let (major, minor, patch, ch) = match maybe_vers {
219         Some((a, b, c, d)) => (a, b, c, d),
220         None => return None
221     };
222
223     let mut pre = vec!();
224     let mut build = vec!();
225
226     let mut ch = ch;
227     if ch == Some('-') {
228         loop {
229             let (id, c) = match take_ident(rdr) {
230                 Some((id, c)) => (id, c),
231                 None => return None
232             };
233             pre.push(id);
234             ch = c;
235             if ch != Some('.') { break; }
236         }
237     }
238
239     if ch == Some('+') {
240         loop {
241             let (id, c) = match take_ident(rdr) {
242                 Some((id, c)) => (id, c),
243                 None => return None
244             };
245             build.push(id);
246             ch = c;
247             if ch != Some('.') { break; }
248         }
249     }
250
251     Some(Version {
252         major: major,
253         minor: minor,
254         patch: patch,
255         pre: pre,
256         build: build,
257     })
258 }
259
260
261 /// Parse a string into a semver object.
262 pub fn parse(s: &str) -> Option<Version> {
263     if !s.is_ascii() {
264         return None;
265     }
266     let s = s.trim();
267     let v = parse_iter(&mut s.chars());
268     match v {
269         Some(v) => {
270             if v.to_str().equiv(&s) {
271                 Some(v)
272             } else {
273                 None
274             }
275         }
276         None => None
277     }
278 }
279
280 #[test]
281 fn test_parse() {
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);
292
293     assert!(parse("1.2.3") == Some(Version {
294         major: 1u,
295         minor: 2u,
296         patch: 3u,
297         pre: vec!(),
298         build: vec!(),
299     }));
300     assert!(parse("  1.2.3  ") == Some(Version {
301         major: 1u,
302         minor: 2u,
303         patch: 3u,
304         pre: vec!(),
305         build: vec!(),
306     }));
307     assert!(parse("1.2.3-alpha1") == Some(Version {
308         major: 1u,
309         minor: 2u,
310         patch: 3u,
311         pre: vec!(AlphaNumeric(~"alpha1")),
312         build: vec!(),
313     }));
314     assert!(parse("  1.2.3-alpha1  ") == Some(Version {
315         major: 1u,
316         minor: 2u,
317         patch: 3u,
318         pre: vec!(AlphaNumeric(~"alpha1")),
319         build: vec!()
320     }));
321     assert!(parse("1.2.3+build5") == Some(Version {
322         major: 1u,
323         minor: 2u,
324         patch: 3u,
325         pre: vec!(),
326         build: vec!(AlphaNumeric(~"build5"))
327     }));
328     assert!(parse("  1.2.3+build5  ") == Some(Version {
329         major: 1u,
330         minor: 2u,
331         patch: 3u,
332         pre: vec!(),
333         build: vec!(AlphaNumeric(~"build5"))
334     }));
335     assert!(parse("1.2.3-alpha1+build5") == Some(Version {
336         major: 1u,
337         minor: 2u,
338         patch: 3u,
339         pre: vec!(AlphaNumeric(~"alpha1")),
340         build: vec!(AlphaNumeric(~"build5"))
341     }));
342     assert!(parse("  1.2.3-alpha1+build5  ") == Some(Version {
343         major: 1u,
344         minor: 2u,
345         patch: 3u,
346         pre: vec!(AlphaNumeric(~"alpha1")),
347         build: vec!(AlphaNumeric(~"build5"))
348     }));
349     assert!(parse("1.2.3-1.alpha1.9+build5.7.3aedf  ") == Some(Version {
350         major: 1u,
351         minor: 2u,
352         patch: 3u,
353         pre: vec!(Numeric(1),AlphaNumeric(~"alpha1"),Numeric(9)),
354         build: vec!(AlphaNumeric(~"build5"),
355                  Numeric(7),
356                  AlphaNumeric(~"3aedf"))
357     }));
358
359 }
360
361 #[test]
362 fn test_eq() {
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"));
368 }
369
370 #[test]
371 fn test_ne() {
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"));
376 }
377
378 #[test]
379 fn test_show() {
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");
384 }
385
386 #[test]
387 fn test_to_str() {
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");
392 }
393
394 #[test]
395 fn test_lt() {
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")));
403 }
404
405 #[test]
406 fn test_le() {
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"));
413 }
414
415 #[test]
416 fn test_gt() {
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")));
424 }
425
426 #[test]
427 fn test_ge() {
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"));
434 }
435
436 #[test]
437 fn test_spec_order() {
438     let vs = ["1.0.0-alpha",
439               "1.0.0-alpha.1",
440               "1.0.0-alpha.beta",
441               "1.0.0-beta",
442               "1.0.0-beta.2",
443               "1.0.0-beta.11",
444               "1.0.0-rc.1",
445               "1.0.0"];
446     let mut i = 1;
447     while i < vs.len() {
448         let a = parse(vs[i-1]).unwrap();
449         let b = parse(vs[i]).unwrap();
450         assert!(a < b);
451         i += 1;
452     }
453 }