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