1 use std::fmt;
2 use std::str::from_utf8;
3 
4 use recognize::*;
5 
6 use common::{self, numeric_identifier};
7 
8 #[derive(Clone, Debug, PartialEq, Eq)]
9 pub struct Version {
10     pub major: u64,
11     pub minor: u64,
12     pub patch: u64,
13     pub pre: Vec<Identifier>,
14     pub build: Vec<Identifier>,
15 }
16 
17 #[derive(Clone, Debug, PartialEq, Eq)]
18 pub enum Identifier {
19     /// An identifier that's solely numbers.
20     Numeric(u64),
21     /// An identifier with letters and numbers.
22     AlphaNumeric(String),
23 }
24 
parse(version: &str) -> Result<Version, String>25 pub fn parse(version: &str) -> Result<Version, String> {
26     let s = version.trim().as_bytes();
27     let mut i = 0;
28     let major = if let Some((major, len)) = numeric_identifier(&s[i..]) {
29         i += len;
30         major
31     } else {
32         return Err("Error parsing major identifier".to_string());
33     };
34     if let Some(len) = b'.'.p(&s[i..]) {
35         i += len;
36     } else {
37         return Err("Expected dot".to_string());
38     }
39     let minor = if let Some((minor, len)) = numeric_identifier(&s[i..]) {
40         i += len;
41         minor
42     } else {
43         return Err("Error parsing minor identifier".to_string());
44     };
45     if let Some(len) = b'.'.p(&s[i..]) {
46         i += len;
47     } else {
48         return Err("Expected dot".to_string());
49     }
50     let patch = if let Some((patch, len)) = numeric_identifier(&s[i..]) {
51         i += len;
52         patch
53     } else {
54         return Err("Error parsing patch identifier".to_string());
55     };
56     let (pre, pre_len) = common::parse_optional_meta(&s[i..], b'-')?;
57     i += pre_len;
58     let (build, build_len) = common::parse_optional_meta(&s[i..], b'+')?;
59     i += build_len;
60     if i != s.len() {
61         return Err("Extra junk after valid version: ".to_string() +
62             from_utf8(&s[i..]).unwrap());
63     }
64     Ok(Version {
65         major: major,
66         minor: minor,
67         patch: patch,
68         pre: pre,
69         build: build,
70     })
71 }
72 
73 impl fmt::Display for Version {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result74     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75         try!(write!(f, "{}.{}.{}", self.major, self.minor, self.patch));
76         if !self.pre.is_empty() {
77             let strs: Vec<_> =
78                 self.pre.iter().map(ToString::to_string).collect();
79             try!(write!(f, "-{}", strs.join(".")));
80         }
81         if !self.build.is_empty() {
82             let strs: Vec<_> =
83                 self.build.iter().map(ToString::to_string).collect();
84             try!(write!(f, "+{}", strs.join(".")));
85         }
86         Ok(())
87     }
88 }
89 
90 impl fmt::Display for Identifier {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result91     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
92         match *self {
93             Identifier::Numeric(ref id) => id.fmt(f),
94             Identifier::AlphaNumeric(ref id) => id.fmt(f),
95         }
96     }
97 }
98 
99 #[cfg(test)]
100 mod tests {
101     use version;
102     use super::*;
103 
104     #[test]
parse_empty()105     fn parse_empty() {
106         let version = "";
107 
108         let parsed = version::parse(version);
109 
110         assert!(parsed.is_err(), "empty string incorrectly considered a valid parse");
111     }
112 
113     #[test]
parse_blank()114     fn parse_blank() {
115         let version = "  ";
116 
117         let parsed = version::parse(version);
118 
119         assert!(parsed.is_err(), "blank string incorrectly considered a valid parse");
120     }
121 
122     #[test]
parse_no_minor_patch()123     fn parse_no_minor_patch() {
124         let version = "1";
125 
126         let parsed = version::parse(version);
127 
128         assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version));
129     }
130 
131     #[test]
parse_no_patch()132     fn parse_no_patch() {
133         let version = "1.2";
134 
135         let parsed = version::parse(version);
136 
137         assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version));
138     }
139 
140     #[test]
parse_empty_pre()141     fn parse_empty_pre() {
142         let version = "1.2.3-";
143 
144         let parsed = version::parse(version);
145 
146         assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version));
147     }
148 
149     #[test]
parse_letters()150     fn parse_letters() {
151         let version = "a.b.c";
152 
153         let parsed = version::parse(version);
154 
155         assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version));
156     }
157 
158     #[test]
parse_with_letters()159     fn parse_with_letters() {
160         let version = "1.2.3 a.b.c";
161 
162         let parsed = version::parse(version);
163 
164         assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version));
165     }
166 
167     #[test]
parse_basic_version()168     fn parse_basic_version() {
169         let version = "1.2.3";
170 
171         let parsed = version::parse(version).unwrap();
172 
173         assert_eq!(1, parsed.major);
174         assert_eq!(2, parsed.minor);
175         assert_eq!(3, parsed.patch);
176     }
177 
178     #[test]
parse_trims_input()179     fn parse_trims_input() {
180         let version = "  1.2.3  ";
181 
182         let parsed = version::parse(version).unwrap();
183 
184         assert_eq!(1, parsed.major);
185         assert_eq!(2, parsed.minor);
186         assert_eq!(3, parsed.patch);
187     }
188 
189     #[test]
parse_no_major_leading_zeroes()190     fn parse_no_major_leading_zeroes() {
191         let version = "01.0.0";
192 
193         let parsed = version::parse(version);
194 
195         assert!(parsed.is_err(), "01 incorrectly considered a valid major version");
196     }
197 
198     #[test]
parse_no_minor_leading_zeroes()199     fn parse_no_minor_leading_zeroes() {
200         let version = "0.01.0";
201 
202         let parsed = version::parse(version);
203 
204         assert!(parsed.is_err(), "01 incorrectly considered a valid minor version");
205     }
206 
207     #[test]
parse_no_patch_leading_zeroes()208     fn parse_no_patch_leading_zeroes() {
209         let version = "0.0.01";
210 
211         let parsed = version::parse(version);
212 
213         assert!(parsed.is_err(), "01 incorrectly considered a valid patch version");
214     }
215 
216     #[test]
parse_no_major_overflow()217     fn parse_no_major_overflow() {
218         let version = "98765432109876543210.0.0";
219 
220         let parsed = version::parse(version);
221 
222         assert!(parsed.is_err(), "98765432109876543210 incorrectly considered a valid major version");
223     }
224 
225     #[test]
parse_no_minor_overflow()226     fn parse_no_minor_overflow() {
227         let version = "0.98765432109876543210.0";
228 
229         let parsed = version::parse(version);
230 
231         assert!(parsed.is_err(), "98765432109876543210 incorrectly considered a valid minor version");
232     }
233 
234     #[test]
parse_no_patch_overflow()235     fn parse_no_patch_overflow() {
236         let version = "0.0.98765432109876543210";
237 
238         let parsed = version::parse(version);
239 
240         assert!(parsed.is_err(), "98765432109876543210 incorrectly considered a valid patch version");
241     }
242 
243     #[test]
parse_basic_prerelease()244     fn parse_basic_prerelease() {
245         let version = "1.2.3-pre";
246 
247         let parsed = version::parse(version).unwrap();
248 
249         let expected_pre = vec![Identifier::AlphaNumeric(String::from("pre"))];
250         assert_eq!(expected_pre, parsed.pre);
251     }
252 
253     #[test]
parse_prerelease_alphanumeric()254     fn parse_prerelease_alphanumeric() {
255         let version = "1.2.3-alpha1";
256 
257         let parsed = version::parse(version).unwrap();
258 
259         let expected_pre = vec![Identifier::AlphaNumeric(String::from("alpha1"))];
260         assert_eq!(expected_pre, parsed.pre);
261     }
262 
263     #[test]
parse_prerelease_zero()264     fn parse_prerelease_zero() {
265         let version = "1.2.3-pre.0";
266 
267         let parsed = version::parse(version).unwrap();
268 
269         let expected_pre = vec![Identifier::AlphaNumeric(String::from("pre")),
270                                 Identifier::Numeric(0)];
271         assert_eq!(expected_pre, parsed.pre);
272     }
273 
274     #[test]
parse_basic_build()275     fn parse_basic_build() {
276         let version = "1.2.3+build";
277 
278         let parsed = version::parse(version).unwrap();
279 
280         let expected_build = vec![Identifier::AlphaNumeric(String::from("build"))];
281         assert_eq!(expected_build, parsed.build);
282     }
283 
284     #[test]
parse_build_alphanumeric()285     fn parse_build_alphanumeric() {
286         let version = "1.2.3+build5";
287 
288         let parsed = version::parse(version).unwrap();
289 
290         let expected_build = vec![Identifier::AlphaNumeric(String::from("build5"))];
291         assert_eq!(expected_build, parsed.build);
292     }
293 
294     #[test]
parse_pre_and_build()295     fn parse_pre_and_build() {
296         let version = "1.2.3-alpha1+build5";
297 
298         let parsed = version::parse(version).unwrap();
299 
300         let expected_pre = vec![Identifier::AlphaNumeric(String::from("alpha1"))];
301         assert_eq!(expected_pre, parsed.pre);
302 
303         let expected_build = vec![Identifier::AlphaNumeric(String::from("build5"))];
304         assert_eq!(expected_build, parsed.build);
305     }
306 
307     #[test]
parse_complex_metadata_01()308     fn parse_complex_metadata_01() {
309         let version = "1.2.3-1.alpha1.9+build5.7.3aedf  ";
310 
311         let parsed = version::parse(version).unwrap();
312 
313         let expected_pre = vec![Identifier::Numeric(1),
314                                 Identifier::AlphaNumeric(String::from("alpha1")),
315                                 Identifier::Numeric(9)];
316         assert_eq!(expected_pre, parsed.pre);
317 
318         let expected_build = vec![Identifier::AlphaNumeric(String::from("build5")),
319                                   Identifier::Numeric(7),
320                                   Identifier::AlphaNumeric(String::from("3aedf"))];
321         assert_eq!(expected_build, parsed.build);
322     }
323 
324     #[test]
parse_complex_metadata_02()325     fn parse_complex_metadata_02() {
326         let version = "0.4.0-beta.1+0851523";
327 
328         let parsed = version::parse(version).unwrap();
329 
330         let expected_pre = vec![Identifier::AlphaNumeric(String::from("beta")),
331                                 Identifier::Numeric(1)];
332         assert_eq!(expected_pre, parsed.pre);
333 
334         let expected_build = vec![Identifier::AlphaNumeric(String::from("0851523"))];
335         assert_eq!(expected_build, parsed.build);
336     }
337 
338     #[test]
parse_metadata_overflow()339     fn parse_metadata_overflow() {
340         let version = "0.4.0-beta.1+98765432109876543210";
341 
342         let parsed = version::parse(version).unwrap();
343 
344         let expected_pre = vec![Identifier::AlphaNumeric(String::from("beta")),
345                                 Identifier::Numeric(1)];
346         assert_eq!(expected_pre, parsed.pre);
347 
348         let expected_build = vec![Identifier::AlphaNumeric(String::from("98765432109876543210"))];
349         assert_eq!(expected_build, parsed.build);
350     }
351 
352     #[test]
parse_regression_01()353     fn parse_regression_01() {
354         let version = "0.0.0-WIP";
355 
356         let parsed = version::parse(version).unwrap();
357 
358         assert_eq!(0, parsed.major);
359         assert_eq!(0, parsed.minor);
360         assert_eq!(0, parsed.patch);
361 
362         let expected_pre = vec![Identifier::AlphaNumeric(String::from("WIP"))];
363         assert_eq!(expected_pre, parsed.pre);
364     }
365 }
366