1 use crate::*;
2 use pest::Parser;
3
4 use std::str::FromStr;
5
6 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
7 pub struct RangeSet {
8 pub ranges: Vec<Range>,
9 pub compat: Compat,
10 }
11
12 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
13 pub enum Compat {
14 Cargo, // default
15 Npm,
16 }
17
18 impl RangeSet {
new() -> RangeSet19 fn new() -> RangeSet {
20 RangeSet {
21 ranges: Vec::new(),
22 compat: Compat::Cargo, // default
23 }
24 }
25
parse(input: &str, compat: Compat) -> Result<Self, String>26 pub fn parse(input: &str, compat: Compat) -> Result<Self, String> {
27 let range_set = match SemverParser::parse(Rule::range_set, input) {
28 Ok(mut parsed) => match parsed.next() {
29 Some(parsed) => parsed,
30 None => return Err(String::from("Could not parse a range set")),
31 },
32 Err(e) => return Err(e.to_string()),
33 };
34
35 from_pair_iterator(range_set, compat)
36 }
37 }
38
39 impl FromStr for RangeSet {
40 type Err = String;
41
from_str(input: &str) -> Result<Self, Self::Err>42 fn from_str(input: &str) -> Result<Self, Self::Err> {
43 // default to cargo-compatible mode
44 RangeSet::parse(input, Compat::Cargo)
45 }
46 }
47
48 /// Converts an iterator of Pairs into a RangeSet
from_pair_iterator( parsed_range_set: pest::iterators::Pair<'_, Rule>, compat: Compat, ) -> Result<RangeSet, String>49 fn from_pair_iterator(
50 parsed_range_set: pest::iterators::Pair<'_, Rule>,
51 compat: Compat,
52 ) -> Result<RangeSet, String> {
53 // First of all, do we have the correct iterator?
54 if parsed_range_set.as_rule() != Rule::range_set {
55 return Err(String::from("Error parsing range set"));
56 }
57
58 // Next, we make a new, empty range
59 let mut range_set = RangeSet::new();
60 range_set.compat = compat;
61
62 // Now we need to parse each range out of the set
63 for record in parsed_range_set.into_inner() {
64 match record.as_rule() {
65 // if we have a range...
66 Rule::range => {
67 // ... let's parse it and push it onto our list of ranges
68 range_set
69 .ranges
70 .push(range::from_pair_iterator(record, compat)?);
71 }
72
73 // we don't need to do anything with the logical ors between ranges
74 Rule::logical_or => (),
75
76 // don't need to do anything with end-of-input
77 Rule::EOI => (),
78
79 // those are the only rules we can have, according to the grammar
80 _ => unreachable!(),
81 }
82 }
83
84 // and that's it!
85 Ok(range_set)
86 }
87
88 #[cfg(test)]
89 mod tests {
90 use super::*;
91
92 macro_rules! range_set_test {
93 ( $name:ident: $input:expr, $($x:tt)* ) => {
94 #[test]
95 fn $name() {
96 let expected_sets = vec![$($x)*];
97
98 let range_set: RangeSet = $input.parse().expect("parse failed");
99
100 assert_eq!(range_set.ranges.len(), expected_sets.len());
101 for it in range_set.ranges.iter().zip(expected_sets.iter()) {
102 let (ai, bi ) = it;
103 assert_eq!(ai.comparator_set.len(), *bi);
104 }
105 }
106 };
107 }
108
109 macro_rules! range_set_nodecompat {
110 ( $name:ident: $input:expr, $($x:tt)* ) => {
111 #[test]
112 fn $name() {
113 let expected_sets = vec![$($x)*];
114
115 let range_set = RangeSet::parse($input, Compat::Npm).expect("parse failed");
116
117 assert_eq!(range_set.ranges.len(), expected_sets.len());
118 for it in range_set.ranges.iter().zip(expected_sets.iter()) {
119 let (ai, bi ) = it;
120 assert_eq!(ai.comparator_set.len(), *bi);
121 }
122 }
123 };
124 }
125
126 macro_rules! should_error {
127 ( $( $name:ident: $value:expr, )* ) => {
128 $(
129 #[test]
130 fn $name() {
131 assert!($value.parse::<RangeSet>().is_err());
132 }
133 )*
134 };
135 }
136
137 range_set_test!( one_range: "=1.2.3", 1 );
138 range_set_test!( one_range_cargo: "1.2.3", 2 ); // this parses as "^1.2.3"
139 range_set_test!( one_range_with_space: " =1.2.3 ", 1 );
140 range_set_test!( two_ranges: ">1.2.3 || =4.5.6", 1, 1 );
141 range_set_test!( two_ranges_with_space: " >1.2.3 || =4.5.6 ", 1, 1 );
142 range_set_test!( two_ranges_with_two_comparators: ">1.2.3 <2.3.4 || >4.5.6 <5.6.7", 2, 2 );
143 range_set_test!( caret_range: "^1.2.3", 2 );
144 range_set_test!( two_empty_ranges: "||", 1, 1 );
145 range_set_test!( two_xranges: "1.2.* || 2.*", 2, 2 );
146 range_set_test!( see_issue_88: "=1.2.3+meta", 1 );
147
148 range_set_nodecompat!( node_one_range: "1.2.3", 1 ); // this parses as "=1.2.3"
149
150 should_error! {
151 err_only_gt: ">",
152 err_only_lt: "<",
153 err_only_lte: "<=",
154 err_only_gte: ">=",
155 err_only_eq: "=",
156 err_only_tilde: "~",
157 err_only_caret: "^",
158 err_leading_0_major: "01.2.3",
159 err_leading_0_minor: "1.02.3",
160 err_leading_0_patch: "1.2.03",
161 err_hyphen_with_gt: "1.2.3 - >3.4.5",
162 err_hyphen_no_2nd_version: "1.2.3 - ",
163 err_no_pre_hyphen: "~1.2.3beta",
164 }
165 }
166