1 use version::Identifier;
2 use recognize::{Recognize, Alt, OneOrMore, Inclusive, OneByte};
3 use std::str::from_utf8;
4
5 // by the time we get here, we know that it's all valid characters, so this doesn't need to return
6 // a result or anything
parse_meta(s: &str) -> Vec<Identifier>7 fn parse_meta(s: &str) -> Vec<Identifier> {
8 // Originally, I wanted to implement this method via calling parse, but parse is tolerant of
9 // leading zeroes, and we want anything with leading zeroes to be considered alphanumeric, not
10 // numeric. So the strategy is to check with a recognizer first, and then call parse once
11 // we've determined that it's a number without a leading zero.
12 s.split(".")
13 .map(|part| {
14 // another wrinkle: we made sure that any number starts with a
15 // non-zero. But there's a problem: an actual zero is a number, yet
16 // gets left out by this heuristic. So let's also check for the
17 // single, lone zero.
18 if is_alpha_numeric(part) {
19 Identifier::AlphaNumeric(part.to_string())
20 } else {
21 // we can unwrap here because we know it is only digits due to the regex
22 Identifier::Numeric(part.parse().unwrap())
23 }
24 }).collect()
25 }
26
27 // parse optional metadata (preceded by the prefix character)
parse_optional_meta(s: &[u8], prefix_char: u8)-> Result<(Vec<Identifier>, usize), String>28 pub fn parse_optional_meta(s: &[u8], prefix_char: u8)-> Result<(Vec<Identifier>, usize), String> {
29 if let Some(len) = prefix_char.p(s) {
30 let start = len;
31 if let Some(len) = letters_numbers_dash_dot(&s[start..]) {
32 let end = start + len;
33 Ok((parse_meta(from_utf8(&s[start..end]).unwrap()), end))
34 } else {
35 Err("Error parsing prerelease".to_string())
36 }
37 } else {
38 Ok((Vec::new(), 0))
39 }
40 }
41
is_alpha_numeric(s: &str) -> bool42 pub fn is_alpha_numeric(s: &str) -> bool {
43 if let Some((_val, len)) = numeric_identifier(s.as_bytes()) {
44 // Return true for number with leading zero
45 // Note: doing it this way also handily makes overflow fail over.
46 len != s.len()
47 } else {
48 true
49 }
50 }
51
52 // Note: could plumb overflow error up to return value as Result
numeric_identifier(s: &[u8]) -> Option<(u64, usize)>53 pub fn numeric_identifier(s: &[u8]) -> Option<(u64, usize)> {
54 if let Some(len) = Alt(b'0', OneOrMore(Inclusive(b'0'..b'9'))).p(s) {
55 from_utf8(&s[0..len]).unwrap().parse().ok().map(|val| (val, len))
56 } else {
57 None
58 }
59 }
60
letters_numbers_dash_dot(s: &[u8]) -> Option<usize>61 pub fn letters_numbers_dash_dot(s: &[u8]) -> Option<usize> {
62 OneOrMore(OneByte(|c| c == b'-' || c == b'.' ||
63 (b'0' <= c && c <= b'9') ||
64 (b'a' <= c && c <= b'z') ||
65 (b'A' <= c && c <= b'Z'))).p(s)
66 }
67