diff options
Diffstat (limited to 'semver-parser/src')
-rw-r--r-- | semver-parser/src/common.rs | 66 | ||||
-rw-r--r-- | semver-parser/src/lib.rs | 8 | ||||
-rw-r--r-- | semver-parser/src/range.rs | 696 | ||||
-rw-r--r-- | semver-parser/src/recognize.rs | 154 | ||||
-rw-r--r-- | semver-parser/src/version.rs | 365 |
5 files changed, 0 insertions, 1289 deletions
diff --git a/semver-parser/src/common.rs b/semver-parser/src/common.rs deleted file mode 100644 index 267b4d9..0000000 --- a/semver-parser/src/common.rs +++ /dev/null @@ -1,66 +0,0 @@ -use version::Identifier; -use recognize::{Recognize, Alt, OneOrMore, Inclusive, OneByte}; -use std::str::from_utf8; - -// by the time we get here, we know that it's all valid characters, so this doesn't need to return -// a result or anything -fn parse_meta(s: &str) -> Vec<Identifier> { - // Originally, I wanted to implement this method via calling parse, but parse is tolerant of - // leading zeroes, and we want anything with leading zeroes to be considered alphanumeric, not - // numeric. So the strategy is to check with a recognizer first, and then call parse once - // we've determined that it's a number without a leading zero. - s.split(".") - .map(|part| { - // another wrinkle: we made sure that any number starts with a - // non-zero. But there's a problem: an actual zero is a number, yet - // gets left out by this heuristic. So let's also check for the - // single, lone zero. - if is_alpha_numeric(part) { - Identifier::AlphaNumeric(part.to_string()) - } else { - // we can unwrap here because we know it is only digits due to the regex - Identifier::Numeric(part.parse().unwrap()) - } - }).collect() -} - -// parse optional metadata (preceded by the prefix character) -pub fn parse_optional_meta(s: &[u8], prefix_char: u8)-> Result<(Vec<Identifier>, usize), String> { - if let Some(len) = prefix_char.p(s) { - let start = len; - if let Some(len) = letters_numbers_dash_dot(&s[start..]) { - let end = start + len; - Ok((parse_meta(from_utf8(&s[start..end]).unwrap()), end)) - } else { - Err("Error parsing prerelease".to_string()) - } - } else { - Ok((Vec::new(), 0)) - } -} - -pub fn is_alpha_numeric(s: &str) -> bool { - if let Some((_val, len)) = numeric_identifier(s.as_bytes()) { - // Return true for number with leading zero - // Note: doing it this way also handily makes overflow fail over. - len != s.len() - } else { - true - } -} - -// Note: could plumb overflow error up to return value as Result -pub fn numeric_identifier(s: &[u8]) -> Option<(u64, usize)> { - if let Some(len) = Alt(b'0', OneOrMore(Inclusive(b'0'..b'9'))).p(s) { - from_utf8(&s[0..len]).unwrap().parse().ok().map(|val| (val, len)) - } else { - None - } -} - -pub fn letters_numbers_dash_dot(s: &[u8]) -> Option<usize> { - OneOrMore(OneByte(|c| c == b'-' || c == b'.' || - (b'0' <= c && c <= b'9') || - (b'a' <= c && c <= b'z') || - (b'A' <= c && c <= b'Z'))).p(s) -} diff --git a/semver-parser/src/lib.rs b/semver-parser/src/lib.rs deleted file mode 100644 index 3b0d8f0..0000000 --- a/semver-parser/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod version; -pub mod range; - -// for private stuff the two share -mod common; - -// for recognizer combinators -mod recognize; diff --git a/semver-parser/src/range.rs b/semver-parser/src/range.rs deleted file mode 100644 index 858be9f..0000000 --- a/semver-parser/src/range.rs +++ /dev/null @@ -1,696 +0,0 @@ -use common::{self, numeric_identifier, letters_numbers_dash_dot}; -use version::Identifier; -use std::str::{FromStr, from_utf8}; -use recognize::*; - -#[derive(Debug)] -pub struct VersionReq { - pub predicates: Vec<Predicate>, -} - -#[derive(PartialEq,Debug)] -pub enum WildcardVersion { - Major, - Minor, - Patch, -} - -#[derive(PartialEq,Debug)] -pub enum Op { - Ex, // Exact - Gt, // Greater than - GtEq, // Greater than or equal to - Lt, // Less than - LtEq, // Less than or equal to - Tilde, // e.g. ~1.0.0 - Compatible, // compatible by definition of semver, indicated by ^ - Wildcard(WildcardVersion), // x.y.*, x.*, * -} - -impl FromStr for Op { - type Err = String; - - fn from_str(s: &str) -> Result<Op, String> { - match s { - "=" => Ok(Op::Ex), - ">" => Ok(Op::Gt), - ">=" => Ok(Op::GtEq), - "<" => Ok(Op::Lt), - "<=" => Ok(Op::LtEq), - "~" => Ok(Op::Tilde), - "^" => Ok(Op::Compatible), - _ => Err(String::from("Could not parse Op")), - } - } -} - -#[derive(PartialEq,Debug)] -pub struct Predicate { - pub op: Op, - pub major: u64, - pub minor: Option<u64>, - pub patch: Option<u64>, - pub pre: Vec<Identifier>, -} - -fn numeric_or_wild(s: &[u8]) -> Option<(Option<u64>, usize)> { - if let Some((val, len)) = numeric_identifier(s) { - Some((Some(val), len)) - } else if let Some(len) = OneOf(b"*xX").p(s) { - Some((None, len)) - } else { - None - } -} - -fn dot_numeric_or_wild(s: &[u8]) -> Option<(Option<u64>, usize)> { - b'.'.p(s).and_then(|len| - numeric_or_wild(&s[len..]).map(|(val, len2)| (val, len + len2)) - ) -} - -fn operation(s: &[u8]) -> Option<(Op, usize)> { - if let Some(len) = "=".p(s) { - Some((Op::Ex, len)) - } else if let Some(len) = ">=".p(s) { - Some((Op::GtEq, len)) - } else if let Some(len) = ">".p(s) { - Some((Op::Gt, len)) - } else if let Some(len) = "<=".p(s) { - Some((Op::LtEq, len)) - } else if let Some(len) = "<".p(s) { - Some((Op::Lt, len)) - } else if let Some(len) = "~".p(s) { - Some((Op::Tilde, len)) - } else if let Some(len) = "^".p(s) { - Some((Op::Compatible, len)) - } else { - None - } -} - -fn whitespace(s: &[u8]) -> Option<usize> { - ZeroOrMore(OneOf(b"\t\r\n ")).p(s) -} - -pub fn parse_predicate(range: &str) -> Result<Predicate, String> { - let s = range.trim().as_bytes(); - let mut i = 0; - let mut operation = if let Some((op, len)) = operation(&s[i..]) { - i += len; - op - } else { - // operations default to Compatible - Op::Compatible - }; - if let Some(len) = whitespace.p(&s[i..]) { - i += len; - } - let major = if let Some((major, len)) = numeric_identifier(&s[i..]) { - i += len; - major - } else { - return Err("Error parsing major version number: ".to_string()); - }; - let minor = if let Some((minor, len)) = dot_numeric_or_wild(&s[i..]) { - i += len; - if minor.is_none() { - operation = Op::Wildcard(WildcardVersion::Minor); - } - minor - } else { - None - }; - let patch = if let Some((patch, len)) = dot_numeric_or_wild(&s[i..]) { - i += len; - if patch.is_none() { - operation = Op::Wildcard(WildcardVersion::Patch); - } - patch - } else { - None - }; - let (pre, pre_len) = common::parse_optional_meta(&s[i..], b'-')?; - i += pre_len; - if let Some(len) = (b'+', letters_numbers_dash_dot).p(&s[i..]) { - i += len; - } - if i != s.len() { - return Err("Extra junk after valid predicate: ".to_string() + - from_utf8(&s[i..]).unwrap()); - } - Ok(Predicate { - op: operation, - major: major, - minor: minor, - patch: patch, - pre: pre, - }) -} - -pub fn parse(ranges: &str) -> Result<VersionReq, String> { - // null is an error - if ranges == "\0" { - return Err(String::from("Null is not a valid VersionReq")); - } - - // an empty range is a major version wildcard - // so is a lone * or x of either capitalization - if (ranges == "") - || (ranges == "*") - || (ranges == "x") - || (ranges == "X") { - return Ok(VersionReq { - predicates: vec![Predicate { - op: Op::Wildcard(WildcardVersion::Major), - major: 0, - minor: None, - patch: None, - pre: Vec::new(), - }], - }); - } - - - let ranges = ranges.trim(); - - let predicates: Result<Vec<_>, String> = ranges - .split(",") - .map(|range| { - parse_predicate(range) - }) - .collect(); - - let predicates = try!(predicates); - - if predicates.len() == 0 { - return Err(String::from("VersionReq did not parse properly")); - } - - Ok(VersionReq { - predicates: predicates, - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use range; - use version::Identifier; - - #[test] - fn test_parsing_default() { - let r = range::parse("1.0.0").unwrap(); - - assert_eq!(Predicate { - op: Op::Compatible, - major: 1, - minor: Some(0), - patch: Some(0), - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - fn test_parsing_exact_01() { - let r = range::parse("=1.0.0").unwrap(); - - assert_eq!(Predicate { - op: Op::Ex, - major: 1, - minor: Some(0), - patch: Some(0), - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - fn test_parsing_exact_02() { - let r = range::parse("=0.9.0").unwrap(); - - assert_eq!(Predicate { - op: Op::Ex, - major: 0, - minor: Some(9), - patch: Some(0), - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - fn test_parsing_exact_03() { - let r = range::parse("=0.1.0-beta2.a").unwrap(); - - assert_eq!(Predicate { - op: Op::Ex, - major: 0, - minor: Some(1), - patch: Some(0), - pre: vec![Identifier::AlphaNumeric(String::from("beta2")), - Identifier::AlphaNumeric(String::from("a"))], - }, - r.predicates[0] - ); - } - - #[test] - pub fn test_parsing_greater_than() { - let r = range::parse("> 1.0.0").unwrap(); - - assert_eq!(Predicate { - op: Op::Gt, - major: 1, - minor: Some(0), - patch: Some(0), - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - pub fn test_parsing_greater_than_01() { - let r = range::parse(">= 1.0.0").unwrap(); - - assert_eq!(Predicate { - op: Op::GtEq, - major: 1, - minor: Some(0), - patch: Some(0), - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - pub fn test_parsing_greater_than_02() { - let r = range::parse(">= 2.1.0-alpha2").unwrap(); - - assert_eq!(Predicate { - op: Op::GtEq, - major: 2, - minor: Some(1), - patch: Some(0), - pre: vec![Identifier::AlphaNumeric(String::from("alpha2"))], - }, - r.predicates[0] - ); - } - - #[test] - pub fn test_parsing_less_than() { - let r = range::parse("< 1.0.0").unwrap(); - - assert_eq!(Predicate { - op: Op::Lt, - major: 1, - minor: Some(0), - patch: Some(0), - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - pub fn test_parsing_less_than_eq() { - let r = range::parse("<= 2.1.0-alpha2").unwrap(); - - assert_eq!(Predicate { - op: Op::LtEq, - major: 2, - minor: Some(1), - patch: Some(0), - pre: vec![Identifier::AlphaNumeric(String::from("alpha2"))], - }, - r.predicates[0] - ); - } - - #[test] - pub fn test_parsing_tilde() { - let r = range::parse("~1").unwrap(); - - assert_eq!(Predicate { - op: Op::Tilde, - major: 1, - minor: None, - patch: None, - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - pub fn test_parsing_compatible() { - let r = range::parse("^0").unwrap(); - - assert_eq!(Predicate { - op: Op::Compatible, - major: 0, - minor: None, - patch: None, - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - fn test_parsing_blank() { - let r = range::parse("").unwrap(); - - assert_eq!(Predicate { - op: Op::Wildcard(WildcardVersion::Major), - major: 0, - minor: None, - patch: None, - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - fn test_parsing_wildcard() { - let r = range::parse("*").unwrap(); - - assert_eq!(Predicate { - op: Op::Wildcard(WildcardVersion::Major), - major: 0, - minor: None, - patch: None, - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - fn test_parsing_x() { - let r = range::parse("x").unwrap(); - - assert_eq!(Predicate { - op: Op::Wildcard(WildcardVersion::Major), - major: 0, - minor: None, - patch: None, - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - fn test_parsing_capital_x() { - let r = range::parse("X").unwrap(); - - assert_eq!(Predicate { - op: Op::Wildcard(WildcardVersion::Major), - major: 0, - minor: None, - patch: None, - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - fn test_parsing_minor_wildcard_star() { - let r = range::parse("1.*").unwrap(); - - assert_eq!(Predicate { - op: Op::Wildcard(WildcardVersion::Minor), - major: 1, - minor: None, - patch: None, - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - fn test_parsing_minor_wildcard_x() { - let r = range::parse("1.x").unwrap(); - - assert_eq!(Predicate { - op: Op::Wildcard(WildcardVersion::Minor), - major: 1, - minor: None, - patch: None, - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - fn test_parsing_minor_wildcard_capital_x() { - let r = range::parse("1.X").unwrap(); - - assert_eq!(Predicate { - op: Op::Wildcard(WildcardVersion::Minor), - major: 1, - minor: None, - patch: None, - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - fn test_parsing_patch_wildcard_star() { - let r = range::parse("1.2.*").unwrap(); - - assert_eq!(Predicate { - op: Op::Wildcard(WildcardVersion::Patch), - major: 1, - minor: Some(2), - patch: None, - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - fn test_parsing_patch_wildcard_x() { - let r = range::parse("1.2.x").unwrap(); - - assert_eq!(Predicate { - op: Op::Wildcard(WildcardVersion::Patch), - major: 1, - minor: Some(2), - patch: None, - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - fn test_parsing_patch_wildcard_capital_x() { - let r = range::parse("1.2.X").unwrap(); - - assert_eq!(Predicate { - op: Op::Wildcard(WildcardVersion::Patch), - major: 1, - minor: Some(2), - patch: None, - pre: Vec::new(), - }, - r.predicates[0] - ); - } - - #[test] - pub fn test_multiple_01() { - let r = range::parse("> 0.0.9, <= 2.5.3").unwrap(); - - assert_eq!(Predicate { - op: Op::Gt, - major: 0, - minor: Some(0), - patch: Some(9), - pre: Vec::new(), - }, - r.predicates[0] - ); - - assert_eq!(Predicate { - op: Op::LtEq, - major: 2, - minor: Some(5), - patch: Some(3), - pre: Vec::new(), - }, - r.predicates[1] - ); - } - - #[test] - pub fn test_multiple_02() { - let r = range::parse("0.3.0, 0.4.0").unwrap(); - - assert_eq!(Predicate { - op: Op::Compatible, - major: 0, - minor: Some(3), - patch: Some(0), - pre: Vec::new(), - }, - r.predicates[0] - ); - - assert_eq!(Predicate { - op: Op::Compatible, - major: 0, - minor: Some(4), - patch: Some(0), - pre: Vec::new(), - }, - r.predicates[1] - ); - } - - #[test] - pub fn test_multiple_03() { - let r = range::parse("<= 0.2.0, >= 0.5.0").unwrap(); - - assert_eq!(Predicate { - op: Op::LtEq, - major: 0, - minor: Some(2), - patch: Some(0), - pre: Vec::new(), - }, - r.predicates[0] - ); - - assert_eq!(Predicate { - op: Op::GtEq, - major: 0, - minor: Some(5), - patch: Some(0), - pre: Vec::new(), - }, - r.predicates[1] - ); - } - - #[test] - pub fn test_multiple_04() { - let r = range::parse("0.1.0, 0.1.4, 0.1.6").unwrap(); - - assert_eq!(Predicate { - op: Op::Compatible, - major: 0, - minor: Some(1), - patch: Some(0), - pre: Vec::new(), - }, - r.predicates[0] - ); - - assert_eq!(Predicate { - op: Op::Compatible, - major: 0, - minor: Some(1), - patch: Some(4), - pre: Vec::new(), - }, - r.predicates[1] - ); - - assert_eq!(Predicate { - op: Op::Compatible, - major: 0, - minor: Some(1), - patch: Some(6), - pre: Vec::new(), - }, - r.predicates[2] - ); - } - - #[test] - pub fn test_multiple_05() { - let r = range::parse(">=0.5.1-alpha3, <0.6").unwrap(); - - assert_eq!(Predicate { - op: Op::GtEq, - major: 0, - minor: Some(5), - patch: Some(1), - pre: vec![Identifier::AlphaNumeric(String::from("alpha3"))], - }, - r.predicates[0] - ); - - assert_eq!(Predicate { - op: Op::Lt, - major: 0, - minor: Some(6), - patch: None, - pre: Vec::new(), - }, - r.predicates[1] - ); - } - - #[test] - fn test_parse_build_metadata_with_predicate() { - assert_eq!(range::parse("^1.2.3+meta").unwrap().predicates[0].op, - Op::Compatible); - assert_eq!(range::parse("~1.2.3+meta").unwrap().predicates[0].op, - Op::Tilde); - assert_eq!(range::parse("=1.2.3+meta").unwrap().predicates[0].op, - Op::Ex); - assert_eq!(range::parse("<=1.2.3+meta").unwrap().predicates[0].op, - Op::LtEq); - assert_eq!(range::parse(">=1.2.3+meta").unwrap().predicates[0].op, - Op::GtEq); - assert_eq!(range::parse("<1.2.3+meta").unwrap().predicates[0].op, - Op::Lt); - assert_eq!(range::parse(">1.2.3+meta").unwrap().predicates[0].op, - Op::Gt); - } - - #[test] - pub fn test_parse_errors() { - assert!(range::parse("\0").is_err()); - assert!(range::parse(">= >= 0.0.2").is_err()); - assert!(range::parse(">== 0.0.2").is_err()); - assert!(range::parse("a.0.0").is_err()); - assert!(range::parse("1.0.0-").is_err()); - assert!(range::parse(">=").is_err()); - assert!(range::parse("> 0.1.0,").is_err()); - assert!(range::parse("> 0.3.0, ,").is_err()); - } - - #[test] - pub fn test_large_major_version() { - assert!(range::parse("18446744073709551617.0.0").is_err()); - } - - #[test] - pub fn test_large_minor_version() { - assert!(range::parse("0.18446744073709551617.0").is_err()); - } - - #[test] - pub fn test_large_patch_version() { - assert!(range::parse("0.0.18446744073709551617").is_err()); - } -} diff --git a/semver-parser/src/recognize.rs b/semver-parser/src/recognize.rs deleted file mode 100644 index c0dd771..0000000 --- a/semver-parser/src/recognize.rs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// -// Licensed under either of MIT or Apache License, Version 2.0, -// at your option. -// -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Simple recognizer combinators. - -// This version is similar to a similar one in the "lang" module of -// xi-editor, but is stripped down to only the needed combinators. - -use std::ops; - -pub trait Recognize { - fn p(&self, s: &[u8]) -> Option<usize>; -} - -impl<F: Fn(&[u8]) -> Option<usize>> Recognize for F { - #[inline(always)] - fn p(&self, s: &[u8]) -> Option<usize> { - self(s) - } -} - -pub struct OneByte<F>(pub F); - -impl<F: Fn(u8) -> bool> Recognize for OneByte<F> { - #[inline(always)] - fn p(&self, s: &[u8]) -> Option<usize> { - if s.is_empty() || !self.0(s[0]) { - None - } else { - Some(1) - } - } -} - -impl Recognize for u8 { - #[inline(always)] - fn p(&self, s: &[u8]) -> Option<usize> { - OneByte(|b| b == *self).p(s) - } -} - -/// Use Inclusive(a..b) to indicate an inclusive range. When a...b syntax becomes -/// stable, we can get rid of this and switch to that. -pub struct Inclusive<T>(pub T); - -impl Recognize for Inclusive<ops::Range<u8>> { - #[inline(always)] - fn p(&self, s: &[u8]) -> Option<usize> { - OneByte(|x| x >= self.0.start && x <= self.0.end).p(s) - } -} - -impl<'a> Recognize for &'a [u8] { - #[inline(always)] - fn p(&self, s: &[u8]) -> Option<usize> { - let len = self.len(); - if s.len() >= len && &s[..len] == *self { - Some(len) - } else { - None - } - } -} - -impl<'a> Recognize for &'a str { - #[inline(always)] - fn p(&self, s: &[u8]) -> Option<usize> { - self.as_bytes().p(s) - } -} - -impl<P1: Recognize, P2: Recognize> Recognize for (P1, P2) { - #[inline(always)] - fn p(&self, s: &[u8]) -> Option<usize> { - self.0.p(s).and_then(|len1| - self.1.p(&s[len1..]).map(|len2| - len1 + len2)) - } -} - -/// Choice from two heterogeneous alternatives. -pub struct Alt<P1, P2>(pub P1, pub P2); - -impl<P1: Recognize, P2: Recognize> Recognize for Alt<P1, P2> { - #[inline(always)] - fn p(&self, s: &[u8]) -> Option<usize> { - self.0.p(s).or_else(|| self.1.p(s)) - } -} - -/// Choice from a homogenous slice of parsers. -pub struct OneOf<'a, P: 'a>(pub &'a [P]); - -impl<'a, P: Recognize> Recognize for OneOf<'a, P> { - #[inline] - fn p(&self, s: &[u8]) -> Option<usize> { - for ref p in self.0 { - if let Some(len) = p.p(s) { - return Some(len); - } - } - None - } -} - -pub struct OneOrMore<P>(pub P); - -impl<P: Recognize> Recognize for OneOrMore<P> { - #[inline] - fn p(&self, s: &[u8]) -> Option<usize> { - let mut i = 0; - let mut count = 0; - while let Some(len) = self.0.p(&s[i..]) { - i += len; - count += 1; - } - if count >= 1 { - Some(i) - } else { - None - } - } -} - -pub struct ZeroOrMore<P>(pub P); - -impl<P: Recognize> Recognize for ZeroOrMore<P> { - #[inline] - fn p(&self, s: &[u8]) -> Option<usize> { - let mut i = 0; - while let Some(len) = self.0.p(&s[i..]) { - i += len; - } - Some(i) - } -} diff --git a/semver-parser/src/version.rs b/semver-parser/src/version.rs deleted file mode 100644 index 570f947..0000000 --- a/semver-parser/src/version.rs +++ /dev/null @@ -1,365 +0,0 @@ -use std::fmt; -use std::str::from_utf8; - -use recognize::*; - -use common::{self, numeric_identifier}; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Version { - pub major: u64, - pub minor: u64, - pub patch: u64, - pub pre: Vec<Identifier>, - pub build: Vec<Identifier>, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Identifier { - /// An identifier that's solely numbers. - Numeric(u64), - /// An identifier with letters and numbers. - AlphaNumeric(String), -} - -pub fn parse(version: &str) -> Result<Version, String> { - let s = version.trim().as_bytes(); - let mut i = 0; - let major = if let Some((major, len)) = numeric_identifier(&s[i..]) { - i += len; - major - } else { - return Err("Error parsing major identifier".to_string()); - }; - if let Some(len) = b'.'.p(&s[i..]) { - i += len; - } else { - return Err("Expected dot".to_string()); - } - let minor = if let Some((minor, len)) = numeric_identifier(&s[i..]) { - i += len; - minor - } else { - return Err("Error parsing minor identifier".to_string()); - }; - if let Some(len) = b'.'.p(&s[i..]) { - i += len; - } else { - return Err("Expected dot".to_string()); - } - let patch = if let Some((patch, len)) = numeric_identifier(&s[i..]) { - i += len; - patch - } else { - return Err("Error parsing patch identifier".to_string()); - }; - let (pre, pre_len) = common::parse_optional_meta(&s[i..], b'-')?; - i += pre_len; - let (build, build_len) = common::parse_optional_meta(&s[i..], b'+')?; - i += build_len; - if i != s.len() { - return Err("Extra junk after valid version: ".to_string() + - from_utf8(&s[i..]).unwrap()); - } - Ok(Version { - major: major, - minor: minor, - patch: patch, - pre: pre, - build: build, - }) -} - -impl fmt::Display for Version { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "{}.{}.{}", self.major, self.minor, self.patch)); - if !self.pre.is_empty() { - let strs: Vec<_> = - self.pre.iter().map(ToString::to_string).collect(); - try!(write!(f, "-{}", strs.join("."))); - } - if !self.build.is_empty() { - let strs: Vec<_> = - self.build.iter().map(ToString::to_string).collect(); - try!(write!(f, "+{}", strs.join("."))); - } - Ok(()) - } -} - -impl fmt::Display for Identifier { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Identifier::Numeric(ref id) => id.fmt(f), - Identifier::AlphaNumeric(ref id) => id.fmt(f), - } - } -} - -#[cfg(test)] -mod tests { - use version; - use super::*; - - #[test] - fn parse_empty() { - let version = ""; - - let parsed = version::parse(version); - - assert!(parsed.is_err(), "empty string incorrectly considered a valid parse"); - } - - #[test] - fn parse_blank() { - let version = " "; - - let parsed = version::parse(version); - - assert!(parsed.is_err(), "blank string incorrectly considered a valid parse"); - } - - #[test] - fn parse_no_minor_patch() { - let version = "1"; - - let parsed = version::parse(version); - - assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version)); - } - - #[test] - fn parse_no_patch() { - let version = "1.2"; - - let parsed = version::parse(version); - - assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version)); - } - - #[test] - fn parse_empty_pre() { - let version = "1.2.3-"; - - let parsed = version::parse(version); - - assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version)); - } - - #[test] - fn parse_letters() { - let version = "a.b.c"; - - let parsed = version::parse(version); - - assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version)); - } - - #[test] - fn parse_with_letters() { - let version = "1.2.3 a.b.c"; - - let parsed = version::parse(version); - - assert!(parsed.is_err(), format!("'{}' incorrectly considered a valid parse", version)); - } - - #[test] - fn parse_basic_version() { - let version = "1.2.3"; - - let parsed = version::parse(version).unwrap(); - - assert_eq!(1, parsed.major); - assert_eq!(2, parsed.minor); - assert_eq!(3, parsed.patch); - } - - #[test] - fn parse_trims_input() { - let version = " 1.2.3 "; - - let parsed = version::parse(version).unwrap(); - - assert_eq!(1, parsed.major); - assert_eq!(2, parsed.minor); - assert_eq!(3, parsed.patch); - } - - #[test] - fn parse_no_major_leading_zeroes() { - let version = "01.0.0"; - - let parsed = version::parse(version); - - assert!(parsed.is_err(), "01 incorrectly considered a valid major version"); - } - - #[test] - fn parse_no_minor_leading_zeroes() { - let version = "0.01.0"; - - let parsed = version::parse(version); - - assert!(parsed.is_err(), "01 incorrectly considered a valid minor version"); - } - - #[test] - fn parse_no_patch_leading_zeroes() { - let version = "0.0.01"; - - let parsed = version::parse(version); - - assert!(parsed.is_err(), "01 incorrectly considered a valid patch version"); - } - - #[test] - fn parse_no_major_overflow() { - let version = "98765432109876543210.0.0"; - - let parsed = version::parse(version); - - assert!(parsed.is_err(), "98765432109876543210 incorrectly considered a valid major version"); - } - - #[test] - fn parse_no_minor_overflow() { - let version = "0.98765432109876543210.0"; - - let parsed = version::parse(version); - - assert!(parsed.is_err(), "98765432109876543210 incorrectly considered a valid minor version"); - } - - #[test] - fn parse_no_patch_overflow() { - let version = "0.0.98765432109876543210"; - - let parsed = version::parse(version); - - assert!(parsed.is_err(), "98765432109876543210 incorrectly considered a valid patch version"); - } - - #[test] - fn parse_basic_prerelease() { - let version = "1.2.3-pre"; - - let parsed = version::parse(version).unwrap(); - - let expected_pre = vec![Identifier::AlphaNumeric(String::from("pre"))]; - assert_eq!(expected_pre, parsed.pre); - } - - #[test] - fn parse_prerelease_alphanumeric() { - let version = "1.2.3-alpha1"; - - let parsed = version::parse(version).unwrap(); - - let expected_pre = vec![Identifier::AlphaNumeric(String::from("alpha1"))]; - assert_eq!(expected_pre, parsed.pre); - } - - #[test] - fn parse_prerelease_zero() { - let version = "1.2.3-pre.0"; - - let parsed = version::parse(version).unwrap(); - - let expected_pre = vec![Identifier::AlphaNumeric(String::from("pre")), - Identifier::Numeric(0)]; - assert_eq!(expected_pre, parsed.pre); - } - - #[test] - fn parse_basic_build() { - let version = "1.2.3+build"; - - let parsed = version::parse(version).unwrap(); - - let expected_build = vec![Identifier::AlphaNumeric(String::from("build"))]; - assert_eq!(expected_build, parsed.build); - } - - #[test] - fn parse_build_alphanumeric() { - let version = "1.2.3+build5"; - - let parsed = version::parse(version).unwrap(); - - let expected_build = vec![Identifier::AlphaNumeric(String::from("build5"))]; - assert_eq!(expected_build, parsed.build); - } - - #[test] - fn parse_pre_and_build() { - let version = "1.2.3-alpha1+build5"; - - let parsed = version::parse(version).unwrap(); - - let expected_pre = vec![Identifier::AlphaNumeric(String::from("alpha1"))]; - assert_eq!(expected_pre, parsed.pre); - - let expected_build = vec![Identifier::AlphaNumeric(String::from("build5"))]; - assert_eq!(expected_build, parsed.build); - } - - #[test] - fn parse_complex_metadata_01() { - let version = "1.2.3-1.alpha1.9+build5.7.3aedf "; - - let parsed = version::parse(version).unwrap(); - - let expected_pre = vec![Identifier::Numeric(1), - Identifier::AlphaNumeric(String::from("alpha1")), - Identifier::Numeric(9)]; - assert_eq!(expected_pre, parsed.pre); - - let expected_build = vec![Identifier::AlphaNumeric(String::from("build5")), - Identifier::Numeric(7), - Identifier::AlphaNumeric(String::from("3aedf"))]; - assert_eq!(expected_build, parsed.build); - } - - #[test] - fn parse_complex_metadata_02() { - let version = "0.4.0-beta.1+0851523"; - - let parsed = version::parse(version).unwrap(); - - let expected_pre = vec![Identifier::AlphaNumeric(String::from("beta")), - Identifier::Numeric(1)]; - assert_eq!(expected_pre, parsed.pre); - - let expected_build = vec![Identifier::AlphaNumeric(String::from("0851523"))]; - assert_eq!(expected_build, parsed.build); - } - - #[test] - fn parse_metadata_overflow() { - let version = "0.4.0-beta.1+98765432109876543210"; - - let parsed = version::parse(version).unwrap(); - - let expected_pre = vec![Identifier::AlphaNumeric(String::from("beta")), - Identifier::Numeric(1)]; - assert_eq!(expected_pre, parsed.pre); - - let expected_build = vec![Identifier::AlphaNumeric(String::from("98765432109876543210"))]; - assert_eq!(expected_build, parsed.build); - } - - #[test] - fn parse_regression_01() { - let version = "0.0.0-WIP"; - - let parsed = version::parse(version).unwrap(); - - assert_eq!(0, parsed.major); - assert_eq!(0, parsed.minor); - assert_eq!(0, parsed.patch); - - let expected_pre = vec![Identifier::AlphaNumeric(String::from("WIP"))]; - assert_eq!(expected_pre, parsed.pre); - } -} |