diff options
Diffstat (limited to 'semver-parser')
-rw-r--r-- | semver-parser/.gitignore | 2 | ||||
-rw-r--r-- | semver-parser/Cargo.toml | 11 | ||||
-rw-r--r-- | semver-parser/LICENSE-APACHE | 201 | ||||
-rw-r--r-- | semver-parser/LICENSE-MIT | 25 | ||||
-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 |
9 files changed, 0 insertions, 1528 deletions
diff --git a/semver-parser/.gitignore b/semver-parser/.gitignore deleted file mode 100644 index a9d37c5..0000000 --- a/semver-parser/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target -Cargo.lock diff --git a/semver-parser/Cargo.toml b/semver-parser/Cargo.toml deleted file mode 100644 index c2be878..0000000 --- a/semver-parser/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "semver-parser" -version = "0.7.0" -authors = ["Steve Klabnik <steve@steveklabnik.com>"] -license = "MIT/Apache-2.0" -repository = "https://github.com/steveklabnik/semver-parser" -homepage = "https://github.com/steveklabnik/semver-parser" -documentation = "https://docs.rs/semver-parser" -description = """ -Parsing of the semver spec. -""" diff --git a/semver-parser/LICENSE-APACHE b/semver-parser/LICENSE-APACHE deleted file mode 100644 index 16fe87b..0000000 --- a/semver-parser/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/semver-parser/LICENSE-MIT b/semver-parser/LICENSE-MIT deleted file mode 100644 index fb7494a..0000000 --- a/semver-parser/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2016 Steve Klabnik - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. 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); - } -} |