rattler_conda_types/version_spec/
constraint.rs1use super::ParseConstraintError;
2use super::RangeOperator;
3use crate::version_spec::parse::constraint_parser;
4use crate::version_spec::{EqualityOperator, StrictRangeOperator};
5use crate::{ParseStrictness, Version};
6use std::str::FromStr;
7
8#[allow(clippy::large_enum_variant)]
10#[derive(Debug, Clone, Eq, PartialEq, Hash)]
11pub enum Constraint {
12 Any,
14
15 Comparison(RangeOperator, Version),
17
18 StrictComparison(StrictRangeOperator, Version),
20
21 Exact(EqualityOperator, Version),
23}
24
25pub(crate) fn is_start_of_version_constraint(c: char) -> bool {
27 matches!(c, '>' | '<' | '=' | '!' | '~')
28}
29
30impl FromStr for Constraint {
31 type Err = ParseConstraintError;
32
33 fn from_str(s: &str) -> Result<Self, Self::Err> {
34 Constraint::from_str(s, ParseStrictness::Lenient)
35 }
36}
37
38impl Constraint {
39 pub fn from_str(
40 input: &str,
41 strictness: ParseStrictness,
42 ) -> Result<Self, ParseConstraintError> {
43 match constraint_parser(strictness)(input) {
44 Ok(("", version)) => Ok(version),
45 Ok((_, _)) => Err(ParseConstraintError::ExpectedEof),
46 Err(nom::Err::Failure(e) | nom::Err::Error(e)) => Err(e),
47 Err(_) => unreachable!("not streaming, so no other error possible"),
48 }
49 }
50}
51
52#[cfg(test)]
53mod test {
54 use super::Constraint;
55 use crate::version_spec::constraint::ParseConstraintError;
56 use crate::version_spec::{EqualityOperator, RangeOperator, StrictRangeOperator};
57 use crate::{ParseStrictness, ParseStrictness::*, Version};
58 use assert_matches::assert_matches;
59 use rstest::rstest;
60 use std::str::FromStr;
61
62 #[rstest]
63 fn test_empty(#[values(Lenient, Strict)] strictness: ParseStrictness) {
64 assert!(matches!(
65 Constraint::from_str("", strictness),
66 Err(ParseConstraintError::InvalidVersion(_))
67 ));
68 }
69
70 #[test]
71 fn test_any() {
72 assert_eq!(Constraint::from_str("*", Lenient), Ok(Constraint::Any));
73 assert_eq!(Constraint::from_str("*", Strict), Ok(Constraint::Any));
74 assert_eq!(Constraint::from_str("*.*", Lenient), Ok(Constraint::Any));
75 assert_eq!(
76 Constraint::from_str("*.*", Strict),
77 Err(ParseConstraintError::InvalidGlob)
78 );
79 }
80
81 #[rstest]
82 fn test_invalid_op(#[values(Lenient, Strict)] strictness: ParseStrictness) {
83 assert_eq!(
84 Constraint::from_str("<>1.2.3", strictness),
85 Err(ParseConstraintError::InvalidOperator(String::from("<>")))
86 );
87 assert_eq!(
88 Constraint::from_str("=!1.2.3", strictness),
89 Err(ParseConstraintError::InvalidOperator(String::from("=!")))
90 );
91 assert_eq!(
92 Constraint::from_str("<!=1.2.3", strictness),
93 Err(ParseConstraintError::InvalidOperator(String::from("<!=")))
94 );
95 assert_eq!(
96 Constraint::from_str("<!>1.2.3", strictness),
97 Err(ParseConstraintError::InvalidOperator(String::from("<!>")))
98 );
99 assert_eq!(
100 Constraint::from_str("!=!1.2.3", strictness),
101 Err(ParseConstraintError::InvalidOperator(String::from("!=!")))
102 );
103 assert_eq!(
104 Constraint::from_str("<=>1.2.3", strictness),
105 Err(ParseConstraintError::InvalidOperator(String::from("<=>")))
106 );
107 assert_eq!(
108 Constraint::from_str("=>1.2.3", strictness),
109 Err(ParseConstraintError::InvalidOperator(String::from("=>")))
110 );
111 }
112
113 #[rstest]
114 fn test_op(#[values(Lenient, Strict)] strictness: ParseStrictness) {
115 assert_eq!(
116 Constraint::from_str(">1.2.3", strictness),
117 Ok(Constraint::Comparison(
118 RangeOperator::Greater,
119 Version::from_str("1.2.3").unwrap(),
120 ))
121 );
122 assert_eq!(
123 Constraint::from_str("<1.2.3", strictness),
124 Ok(Constraint::Comparison(
125 RangeOperator::Less,
126 Version::from_str("1.2.3").unwrap(),
127 ))
128 );
129 assert_eq!(
130 Constraint::from_str("=1.2.3", strictness),
131 Ok(Constraint::StrictComparison(
132 StrictRangeOperator::StartsWith,
133 Version::from_str("1.2.3").unwrap(),
134 ))
135 );
136 assert_eq!(
137 Constraint::from_str("==1.2.3", strictness),
138 Ok(Constraint::Exact(
139 EqualityOperator::Equals,
140 Version::from_str("1.2.3").unwrap(),
141 ))
142 );
143 assert_eq!(
144 Constraint::from_str("!=1.2.3", strictness),
145 Ok(Constraint::Exact(
146 EqualityOperator::NotEquals,
147 Version::from_str("1.2.3").unwrap(),
148 ))
149 );
150 assert_eq!(
151 Constraint::from_str("~=1.2.3", strictness),
152 Ok(Constraint::StrictComparison(
153 StrictRangeOperator::Compatible,
154 Version::from_str("1.2.3").unwrap(),
155 ))
156 );
157 assert_eq!(
158 Constraint::from_str(">=1.2.3", strictness),
159 Ok(Constraint::Comparison(
160 RangeOperator::GreaterEquals,
161 Version::from_str("1.2.3").unwrap(),
162 ))
163 );
164 assert_eq!(
165 Constraint::from_str("<=1.2.3", strictness),
166 Ok(Constraint::Comparison(
167 RangeOperator::LessEquals,
168 Version::from_str("1.2.3").unwrap(),
169 ))
170 );
171 assert_eq!(
172 Constraint::from_str(">=1!1.2", strictness),
173 Ok(Constraint::Comparison(
174 RangeOperator::GreaterEquals,
175 Version::from_str("1!1.2").unwrap(),
176 ))
177 );
178 }
179
180 #[test]
181 fn test_glob_op_lenient() {
182 assert_eq!(
183 Constraint::from_str("=1.2.*", Lenient),
184 Ok(Constraint::StrictComparison(
185 StrictRangeOperator::StartsWith,
186 Version::from_str("1.2").unwrap(),
187 ))
188 );
189 assert_eq!(
190 Constraint::from_str("!=1.2.*", Lenient),
191 Ok(Constraint::StrictComparison(
192 StrictRangeOperator::NotStartsWith,
193 Version::from_str("1.2").unwrap(),
194 ))
195 );
196 assert_eq!(
197 Constraint::from_str(">=1.2.*", Lenient),
198 Ok(Constraint::Comparison(
199 RangeOperator::GreaterEquals,
200 Version::from_str("1.2").unwrap(),
201 ))
202 );
203 assert_eq!(
204 Constraint::from_str("==1.2.*", Lenient),
205 Ok(Constraint::Exact(
206 EqualityOperator::Equals,
207 Version::from_str("1.2").unwrap(),
208 ))
209 );
210 assert_eq!(
211 Constraint::from_str(">1.2.*", Lenient),
212 Ok(Constraint::Comparison(
213 RangeOperator::GreaterEquals,
214 Version::from_str("1.2").unwrap(),
215 ))
216 );
217 assert_eq!(
218 Constraint::from_str("<=1.2.*", Lenient),
219 Ok(Constraint::Comparison(
220 RangeOperator::LessEquals,
221 Version::from_str("1.2").unwrap(),
222 ))
223 );
224 assert_eq!(
225 Constraint::from_str("<1.2.*", Lenient),
226 Ok(Constraint::Comparison(
227 RangeOperator::Less,
228 Version::from_str("1.2").unwrap(),
229 ))
230 );
231 }
232
233 #[test]
234 fn test_glob_op_strict() {
235 assert_matches!(
236 Constraint::from_str("=1.2.*", Strict),
237 Err(ParseConstraintError::GlobVersionIncompatibleWithOperator(_))
238 );
239 assert_eq!(
240 Constraint::from_str("!=1.2.*", Lenient),
241 Ok(Constraint::StrictComparison(
242 StrictRangeOperator::NotStartsWith,
243 Version::from_str("1.2").unwrap(),
244 ))
245 );
246 assert_matches!(
247 Constraint::from_str(">=1.2.*", Strict),
248 Err(ParseConstraintError::GlobVersionIncompatibleWithOperator(_))
249 );
250 assert_matches!(
251 Constraint::from_str("==1.2.*", Strict),
252 Err(ParseConstraintError::GlobVersionIncompatibleWithOperator(_))
253 );
254 assert_matches!(
255 Constraint::from_str(">1.2.*", Strict),
256 Err(ParseConstraintError::GlobVersionIncompatibleWithOperator(_))
257 );
258 assert_matches!(
259 Constraint::from_str("<=1.2.*", Strict),
260 Err(ParseConstraintError::GlobVersionIncompatibleWithOperator(_))
261 );
262 assert_matches!(
263 Constraint::from_str("<1.2.*", Strict),
264 Err(ParseConstraintError::GlobVersionIncompatibleWithOperator(_))
265 );
266 }
267
268 #[rstest]
269 fn test_starts_with(#[values(Lenient, Strict)] strictness: ParseStrictness) {
270 assert_eq!(
271 Constraint::from_str("1.2.*", strictness),
272 Ok(Constraint::StrictComparison(
273 StrictRangeOperator::StartsWith,
274 Version::from_str("1.2").unwrap(),
275 ))
276 );
277 }
278
279 #[rstest]
280 fn test_exact(#[values(Lenient, Strict)] strictness: ParseStrictness) {
281 assert_eq!(
282 Constraint::from_str("==1.2.3", strictness),
283 Ok(Constraint::Exact(
284 EqualityOperator::Equals,
285 Version::from_str("1.2.3").unwrap(),
286 ))
287 );
288 }
289
290 #[rstest]
291 fn test_regex(#[values(Lenient, Strict)] strictness: ParseStrictness) {
292 assert_eq!(
293 Constraint::from_str("^1.2.3", strictness),
294 Err(ParseConstraintError::UnterminatedRegex)
295 );
296 assert_eq!(
297 Constraint::from_str("1.2.3$", strictness),
298 Err(ParseConstraintError::UnterminatedRegex)
299 );
300 assert_eq!(
301 Constraint::from_str("1.*.3", strictness),
302 Err(ParseConstraintError::RegexConstraintsNotSupported)
303 );
304 }
305}