alpm_types/version/requirement.rs
1//! Version requirement declarations and comparisons based on them.
2
3use std::{
4 cmp::Ordering,
5 fmt::{Display, Formatter},
6 str::FromStr,
7};
8
9use alpm_parsers::iter_str_context;
10use serde::{Deserialize, Serialize};
11use strum::VariantNames;
12use winnow::{
13 ModalResult,
14 Parser,
15 combinator::{alt, eof, fail, seq},
16 error::{StrContext, StrContextValue},
17 token::take_while,
18};
19
20use crate::{Error, Version};
21
22/// A version requirement, e.g. for a dependency package.
23///
24/// It consists of a target version and a comparison function. A version requirement of `>=1.5` has
25/// a target version of `1.5` and a comparison function of [`VersionComparison::GreaterOrEqual`].
26/// See [alpm-comparison] for details on the format.
27///
28/// ## Examples
29///
30/// ```
31/// use std::str::FromStr;
32///
33/// use alpm_types::{Version, VersionComparison, VersionRequirement};
34///
35/// # fn main() -> Result<(), alpm_types::Error> {
36/// let requirement = VersionRequirement::from_str(">=1.5")?;
37///
38/// assert_eq!(requirement.comparison, VersionComparison::GreaterOrEqual);
39/// assert_eq!(requirement.version, Version::from_str("1.5")?);
40/// # Ok(())
41/// # }
42/// ```
43///
44/// [alpm-comparison]: https://alpm.archlinux.page/specifications/alpm-comparison.7.html
45#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
46pub struct VersionRequirement {
47 /// Version comparison function
48 pub comparison: VersionComparison,
49 /// Target version
50 pub version: Version,
51}
52
53impl VersionRequirement {
54 /// Create a new `VersionRequirement`
55 pub fn new(comparison: VersionComparison, version: Version) -> Self {
56 VersionRequirement {
57 comparison,
58 version,
59 }
60 }
61
62 /// Returns `true` if the requirement is satisfied by the given package version.
63 ///
64 /// ## Examples
65 ///
66 /// ```
67 /// use std::str::FromStr;
68 ///
69 /// use alpm_types::{Version, VersionRequirement};
70 ///
71 /// # fn main() -> Result<(), alpm_types::Error> {
72 /// let requirement = VersionRequirement::from_str(">=1.5-3")?;
73 ///
74 /// assert!(!requirement.is_satisfied_by(&Version::from_str("1.5")?));
75 /// assert!(requirement.is_satisfied_by(&Version::from_str("1.5-3")?));
76 /// assert!(requirement.is_satisfied_by(&Version::from_str("1.6")?));
77 /// assert!(requirement.is_satisfied_by(&Version::from_str("2:1.0")?));
78 /// assert!(!requirement.is_satisfied_by(&Version::from_str("1.0")?));
79 ///
80 /// // If pkgrel is not specified in the requirement, it is ignored in the comparison.
81 /// let requirement = VersionRequirement::from_str("=1.5")?;
82 /// assert!(requirement.is_satisfied_by(&Version::from_str("1.5-3")?));
83 /// # Ok(())
84 /// # }
85 /// ```
86 pub fn is_satisfied_by(&self, ver: &Version) -> bool {
87 // If the requirement does not specify a pkgrel, we ignore it in the comparison.
88 // so that `foo=1` can be satisfied by `foo=1-1`.
89 let other_version = if self.version.pkgrel.is_none() {
90 &Version {
91 pkgrel: None,
92 ..ver.clone()
93 }
94 } else {
95 ver
96 };
97 self.comparison
98 .is_compatible_with(other_version.cmp(&self.version))
99 }
100
101 /// Recognizes a [`VersionRequirement`] in a string slice.
102 ///
103 /// Consumes all of its input.
104 ///
105 /// # Errors
106 ///
107 /// Returns an error if `input` is not a valid _alpm-comparison_.
108 pub fn parser(input: &mut &str) -> ModalResult<Self> {
109 seq!(Self {
110 comparison: take_while(1.., ('<', '>', '='))
111 // add context here because otherwise take_while can fail and provide no information
112 .context(StrContext::Expected(StrContextValue::Description(
113 "version comparison operator"
114 )))
115 .and_then(VersionComparison::parser),
116 version: Version::parser,
117 })
118 .parse_next(input)
119 }
120
121 /// Checks whether another [`VersionRequirement`] forms an intersection with this one.
122 ///
123 /// The intersection operation `∩` on versions simply checks if there is _any_ possible set of
124 /// versions that can exist while upholding the constraints (e.g. `>`/`<=`) on both versions.
125 ///
126 /// # Examples
127 ///
128 /// - The expression `<3 ∩ <1` forms the intersection of all versions `<1`
129 /// - The expression `<2 ∩ >1` forms the intersection `X` of all versions `1<X<2`
130 /// - The expression `=2 ∩ <3` forms the intersection of the exact version `2`
131 ///
132 /// ```
133 /// use std::str::FromStr;
134 ///
135 /// use alpm_types::VersionRequirement;
136 ///
137 /// # fn main() -> testresult::TestResult {
138 /// let requirement: VersionRequirement = "<1".parse()?;
139 /// assert!(requirement.is_intersection(&"<0.1".parse()?));
140 ///
141 /// let requirement: VersionRequirement = "<2".parse()?;
142 /// assert!(requirement.is_intersection(&">1".parse()?));
143 ///
144 /// let requirement: VersionRequirement = "=2".parse()?;
145 /// assert!(!requirement.is_intersection(&"<3".parse()?));
146 /// # Ok(())
147 /// # }
148 /// ```
149 pub fn is_intersection(&self, other: &VersionRequirement) -> bool {
150 // This documentation uses the `∩` set intersection operator to better visualize examples.
151 //
152 // In the following, we need to consider the ordering relationship between the actual
153 // versions of the two `VersionRequirement`s.
154 // If we have `self = ">1.0.1"` and `other = "<2"`, this handles the part of
155 // `"1.0.1".cmp("2")`.
156 let version_comparison = self.version.cmp(&other.version);
157
158 match self.comparison {
159 // Consider the case where we have a `Less`, e.g. `<1`.
160 VersionComparison::Less => {
161 match version_comparison {
162 // The other version is greater, so its comparison must be "Less" or
163 // "LessOrEqual" to form an intersection.
164 //
165 // Example:
166 // - `<1.0.1 ∩ <2` forms the intersection of all versions `<1.0.1`
167 Ordering::Less => matches!(
168 other.comparison,
169 VersionComparison::Less | VersionComparison::LessOrEqual
170 ),
171 // Both versions are matching. The comparison for other must be "Less" or
172 // "LessOrEqual"
173 //
174 // Example:
175 // - `<=2 ∩ <2` forms the intersection of all versions `<2`
176 // - `<2 ∩ <=2` forms the intersection of all versions `<2`
177 Ordering::Equal => matches!(
178 other.comparison,
179 VersionComparison::Less | VersionComparison::LessOrEqual
180 ),
181
182 // The other version is smaller.
183 // Since `self` enforces the `Less` constraint, there will always be at least
184 // **some** intersection.
185 //
186 // Example: Even if `other` also has a "Less" constraint, the expression
187 // `<3 ∩ <1` forms the intersection of all versions `<1`
188 Ordering::Greater => true,
189 }
190 }
191 // Consider the case where we have a `LessOrEqual`, e.g. `<=1`.
192 VersionComparison::LessOrEqual => {
193 match version_comparison {
194 // The other version is greater, so its comparison must be "Less" or
195 // "LessOrEqual" to form an intersection.
196 //
197 // Example:
198 // - `>=1.0.1 ∩ <2` forms the intersection of all versions `1.0.1<=X<2`
199 // - `>= 1.0.1 <= 1.2` forms the intersection of all versions `1.0.1<=X<=1.2`
200 Ordering::Less => matches!(
201 other.comparison,
202 VersionComparison::Less | VersionComparison::LessOrEqual
203 ),
204 // Both versions are matching, the comparison for other must be either "Less"
205 // or one of "Equal", "LessOrEqual", or "GreaterOrEqual".
206 // Any `other` "*Equal" constraint will directly match the `self`
207 // "Less**Equal**" constraint.
208 //
209 // Examples:
210 // - `<=1 ∩ >=1` forms the intersection of the version `1`
211 // - `<=1 ∩ <1` forms the intersection of all version `<1`
212 // - `<=1 ∩ <=1` forms the intersection of all version `<=1`
213 Ordering::Equal => matches!(
214 other.comparison,
215 VersionComparison::Less
216 | VersionComparison::LessOrEqual
217 | VersionComparison::Equal
218 | VersionComparison::GreaterOrEqual
219 ),
220 // The other version is smaller.
221 // Since `self` enforces the `Less` constraint, there will always be at least
222 // **some** intersection.
223 //
224 // Example: Even if `other` also has a "Less" constraint, the expression
225 // `=<3 ∩ <1` forms the intersection of all versions `<1`
226 Ordering::Greater => true,
227 }
228 }
229 // Consider the case where we have a `Equal`, e.g. `=1`.
230 VersionComparison::Equal => match version_comparison {
231 // Both versions are matching, the comparison for `other` must be
232 // "LessOrEqual", "Equal", or "GreaterOrEqual" to match the "Equal" constraint on
233 // `self`
234 //
235 // Examples:
236 // - `=1 ∩ >=1` forms the intersection of the version `1`
237 // - `=1 ∩ <=1` forms the intersection of the version `1`
238 // - `=2 ∩ =2` forms the intersection of the version `2`
239 Ordering::Equal => matches!(
240 other.comparison,
241 VersionComparison::LessOrEqual
242 | VersionComparison::Equal
243 | VersionComparison::GreaterOrEqual
244 ),
245 // The other version must be greater or smaller, so it can be inherently not be
246 // equal.
247 Ordering::Less | Ordering::Greater => false,
248 },
249 // Consider the case where we have a `GreaterOrEqual`, e.g. `>=1`.
250 VersionComparison::GreaterOrEqual => match version_comparison {
251 // The other version is greater.
252 // Since `self` enforces a `Greater` constraint, so there will always be at least
253 // **some** intersection.
254 //
255 // Example: Even if `other` also has a "Less" constraint, the expression
256 // `>=1 ∩ <3` forms the intersection of all versions `1<=X<3`
257 Ordering::Less => true,
258 // Both versions are matching, the comparison for other must be either "Greater"
259 // or one of "Equal", "LessOrEqual", or "GreaterOrEqual".
260 // Any `other` "*Equal" constraint will directly match `self`'s
261 // "LesserOr**Equal**" constraint.
262 //
263 // Examples:
264 // - `>=1 ∩ <=1` forms the intersection of the version `1`
265 // - `>=1 ∩ >1` forms the intersection of all version `>1`
266 // - `>=1 ∩ >=1` forms the intersection of all version `>=1`
267 Ordering::Equal => matches!(
268 other.comparison,
269 VersionComparison::LessOrEqual
270 | VersionComparison::Equal
271 | VersionComparison::GreaterOrEqual
272 | VersionComparison::Greater
273 ),
274 // The other version is smaller, so its comparison must be at least "Greater" or
275 // "GreaterOrEqual" to form an intersection.
276 //
277 // Example:
278 // - `>=2 ∩ >1.1` forms the intersection of all versions `>=2`
279 Ordering::Greater => matches!(
280 other.comparison,
281 VersionComparison::GreaterOrEqual | VersionComparison::Greater
282 ),
283 },
284 // Consider the case where we have a `Greater`, e.g. `>1`.
285 VersionComparison::Greater => {
286 match version_comparison {
287 // The other version is greater.
288 // Since `self` enforces a `Greater` constraint, so there will always be at
289 // least **some** intersection.
290 //
291 // Example: Even if `other` also has a "Less" constraint, the expression
292 // `>1 ∩ <3` forms the intersection of all versions `1<X<3`
293 Ordering::Less => true,
294 // Both versions are matching. The comparison for other must be "Greater" or
295 // "GreaterOrEqual"
296 //
297 // Example:
298 // - `>2 ∩ >2` forms the intersection of all versions `>2`
299 // - `>2 ∩ >=2` forms the intersection of all versions `>2`
300 Ordering::Equal => matches!(
301 other.comparison,
302 VersionComparison::GreaterOrEqual | VersionComparison::Greater
303 ),
304 // The other version is smaller, so its comparison must be at least "Greater" or
305 // "GreaterOrEqual" to form an intersection.
306 //
307 // Example:
308 // - `>2 ∩ >=1.1` forms the intersection of all versions `>=2`
309 Ordering::Greater => matches!(
310 other.comparison,
311 VersionComparison::GreaterOrEqual | VersionComparison::Greater
312 ),
313 }
314 }
315 }
316 }
317}
318
319impl Display for VersionRequirement {
320 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
321 write!(f, "{}{}", self.comparison, self.version)
322 }
323}
324
325impl FromStr for VersionRequirement {
326 type Err = Error;
327
328 /// Creates a new [`VersionRequirement`] from a string slice.
329 ///
330 /// Delegates to [`VersionRequirement::parser`].
331 ///
332 /// # Errors
333 ///
334 /// Returns an error if [`VersionRequirement::parser`] fails.
335 fn from_str(s: &str) -> Result<Self, Self::Err> {
336 Ok(Self::parser.parse(s)?)
337 }
338}
339
340/// Specifies the comparison function for a [`VersionRequirement`].
341///
342/// The package version can be required to be:
343/// - less than (`<`)
344/// - less than or equal to (`<=`)
345/// - equal to (`=`)
346/// - greater than or equal to (`>=`)
347/// - greater than (`>`)
348///
349/// the specified version.
350///
351/// See [alpm-comparison] for details on the format.
352///
353/// ## Note
354///
355/// The variants of this enum are sorted in a way, that prefers the two-letter comparators over
356/// the one-letter ones.
357/// This is because when splitting a string on the string representation of [`VersionComparison`]
358/// variant and relying on the ordering of [`strum::EnumIter`], the two-letter comparators must be
359/// checked before checking the one-letter ones to yield robust results.
360///
361/// [alpm-comparison]: https://alpm.archlinux.page/specifications/alpm-comparison.7.html
362#[derive(
363 strum::AsRefStr,
364 Clone,
365 Copy,
366 Debug,
367 strum::Display,
368 strum::EnumIter,
369 PartialEq,
370 Eq,
371 strum::VariantNames,
372 Serialize,
373 Deserialize,
374)]
375pub enum VersionComparison {
376 /// Less than or equal to
377 #[strum(to_string = "<=")]
378 LessOrEqual,
379
380 /// Greater than or equal to
381 #[strum(to_string = ">=")]
382 GreaterOrEqual,
383
384 /// Equal to
385 #[strum(to_string = "=")]
386 Equal,
387
388 /// Less than
389 #[strum(to_string = "<")]
390 Less,
391
392 /// Greater than
393 #[strum(to_string = ">")]
394 Greater,
395}
396
397impl VersionComparison {
398 /// Returns `true` if the result of a comparison between the actual and required package
399 /// versions satisfies the comparison function.
400 fn is_compatible_with(self, ord: Ordering) -> bool {
401 match (self, ord) {
402 (VersionComparison::Less, Ordering::Less)
403 | (VersionComparison::LessOrEqual, Ordering::Less | Ordering::Equal)
404 | (VersionComparison::Equal, Ordering::Equal)
405 | (VersionComparison::GreaterOrEqual, Ordering::Greater | Ordering::Equal)
406 | (VersionComparison::Greater, Ordering::Greater) => true,
407
408 (VersionComparison::Less, Ordering::Equal | Ordering::Greater)
409 | (VersionComparison::LessOrEqual, Ordering::Greater)
410 | (VersionComparison::Equal, Ordering::Less | Ordering::Greater)
411 | (VersionComparison::GreaterOrEqual, Ordering::Less)
412 | (VersionComparison::Greater, Ordering::Less | Ordering::Equal) => false,
413 }
414 }
415
416 /// Recognizes a [`VersionComparison`] in a string slice.
417 ///
418 /// Consumes all of its input.
419 ///
420 /// # Errors
421 ///
422 /// Returns an error if `input` is not a valid _alpm-comparison_.
423 pub fn parser(input: &mut &str) -> ModalResult<Self> {
424 alt((
425 // insert eofs here (instead of after alt call) so correct error message is thrown
426 ("<=", eof).value(Self::LessOrEqual),
427 (">=", eof).value(Self::GreaterOrEqual),
428 ("=", eof).value(Self::Equal),
429 ("<", eof).value(Self::Less),
430 (">", eof).value(Self::Greater),
431 fail.context(StrContext::Label("comparison operator"))
432 .context_with(iter_str_context!([VersionComparison::VARIANTS])),
433 ))
434 .parse_next(input)
435 }
436}
437
438impl FromStr for VersionComparison {
439 type Err = Error;
440
441 /// Creates a new [`VersionComparison`] from a string slice.
442 ///
443 /// Delegates to [`VersionComparison::parser`].
444 ///
445 /// # Errors
446 ///
447 /// Returns an error if [`VersionComparison::parser`] fails.
448 fn from_str(s: &str) -> Result<Self, Self::Err> {
449 Ok(Self::parser.parse(s)?)
450 }
451}
452
453#[cfg(test)]
454mod tests {
455 use rstest::rstest;
456 use testresult::TestResult;
457
458 use super::*;
459 /// Ensure that valid version comparison strings can be parsed.
460 #[rstest]
461 #[case("<", VersionComparison::Less)]
462 #[case("<=", VersionComparison::LessOrEqual)]
463 #[case("=", VersionComparison::Equal)]
464 #[case(">=", VersionComparison::GreaterOrEqual)]
465 #[case(">", VersionComparison::Greater)]
466 fn valid_version_comparison(#[case] comparison: &str, #[case] expected: VersionComparison) {
467 assert_eq!(comparison.parse(), Ok(expected));
468 }
469
470 /// Ensure that invalid version comparisons will throw an error.
471 #[rstest]
472 #[case("", "invalid comparison operator")]
473 #[case("<<", "invalid comparison operator")]
474 #[case("==", "invalid comparison operator")]
475 #[case("!=", "invalid comparison operator")]
476 #[case(" =", "invalid comparison operator")]
477 #[case("= ", "invalid comparison operator")]
478 #[case("<1", "invalid comparison operator")]
479 fn invalid_version_comparison(#[case] comparison: &str, #[case] err_snippet: &str) {
480 let Err(Error::ParseError(err_msg)) = VersionComparison::from_str(comparison) else {
481 panic!("'{comparison}' did not fail as expected")
482 };
483 assert!(
484 err_msg.contains(err_snippet),
485 "Error:\n=====\n{err_msg}\n=====\nshould contain snippet:\n\n{err_snippet}"
486 );
487 }
488
489 /// Test successful parsing for version requirement strings.
490 #[rstest]
491 #[case("=1", VersionRequirement {
492 comparison: VersionComparison::Equal,
493 version: Version::from_str("1").unwrap(),
494 })]
495 #[case("<=42:abcd-2.4", VersionRequirement {
496 comparison: VersionComparison::LessOrEqual,
497 version: Version::from_str("42:abcd-2.4").unwrap(),
498 })]
499 #[case(">3.1", VersionRequirement {
500 comparison: VersionComparison::Greater,
501 version: Version::from_str("3.1").unwrap(),
502 })]
503 fn valid_version_requirement(#[case] requirement: &str, #[case] expected: VersionRequirement) {
504 assert_eq!(
505 requirement.parse(),
506 Ok(expected),
507 "Expected successful parse for version requirement '{requirement}'"
508 );
509 }
510
511 #[rstest]
512 #[case::bad_operator("<>3.1", "invalid comparison operator")]
513 #[case::no_operator("3.1", "expected version comparison operator")]
514 #[case::arrow_operator("=>3.1", "invalid comparison operator")]
515 #[case::no_version("<=", "expected pkgver string")]
516 fn invalid_version_requirement(#[case] requirement: &str, #[case] err_snippet: &str) {
517 let Err(Error::ParseError(err_msg)) = VersionRequirement::from_str(requirement) else {
518 panic!("'{requirement}' erroneously parsed as VersionRequirement")
519 };
520 assert!(
521 err_msg.contains(err_snippet),
522 "Error:\n=====\n{err_msg}\n=====\nshould contain snippet:\n\n{err_snippet}"
523 );
524 }
525
526 #[rstest]
527 #[case("<3.1>3.2", "invalid pkgver character")]
528 fn invalid_version_requirement_pkgver_parse(
529 #[case] requirement: &str,
530 #[case] err_snippet: &str,
531 ) {
532 let Err(Error::ParseError(err_msg)) = VersionRequirement::from_str(requirement) else {
533 panic!("'{requirement}' erroneously parsed as VersionRequirement")
534 };
535 assert!(
536 err_msg.contains(err_snippet),
537 "Error:\n=====\n{err_msg}\n=====\nshould contain snippet:\n\n{err_snippet}"
538 );
539 }
540
541 /// Check whether a version requirement (>= 1.0) is fulfilled by a given version string.
542 #[rstest]
543 #[case("=1", "1", true)]
544 #[case("=1", "1.0", false)]
545 #[case("=1", "1-1", true)]
546 #[case("=1", "1:1", false)]
547 #[case("=1", "0.9", false)]
548 #[case("<42", "41", true)]
549 #[case("<42", "42", false)]
550 #[case("<42", "43", false)]
551 #[case("<=42", "41", true)]
552 #[case("<=42", "42", true)]
553 #[case("<=42", "43", false)]
554 #[case(">42", "41", false)]
555 #[case(">42", "42", false)]
556 #[case(">42", "43", true)]
557 #[case(">=42", "41", false)]
558 #[case(">=42", "42", true)]
559 #[case(">=42", "43", true)]
560 fn version_requirement_satisfied(
561 #[case] requirement: &str,
562 #[case] version: &str,
563 #[case] result: bool,
564 ) {
565 let requirement = VersionRequirement::from_str(requirement).unwrap();
566 let version = Version::from_str(version).unwrap();
567 assert_eq!(requirement.is_satisfied_by(&version), result);
568 }
569
570 #[rstest]
571 #[case::self_less_matching_other_less("<1", "<1")]
572 #[case::self_less_matching_other_less_or_equal("<1", "<=1")]
573 #[case::self_less_bigger_other_less("<1", "<2")]
574 #[case::self_less_bigger_other_less_or_equal("<1", "<=2")]
575 #[case::self_less_smaller_other_less("<1", "<0.1")]
576 #[case::self_less_smaller_other_less_or_equal("<1", "<=0.1")]
577 #[case::self_less_smaller_other_equal("<1", "=0.1")]
578 #[case::self_less_smaller_other_greater_or_equal("<1", ">=0.1")]
579 #[case::self_less_smaller_other_greater("<1", ">0.1")]
580 #[case::self_less_smaller_other_equal("<1", "=0.1")]
581 #[case::self_less_or_equal_matching_other_less("<=1", "<1")]
582 #[case::self_less_or_equal_matching_other_less_or_equal("<=1", "<=1")]
583 #[case::self_less_or_equal_matching_other_equal("<=1", "=1")]
584 #[case::self_less_or_equal_matching_other_greater_or_equal("<=1", ">=1")]
585 #[case::self_less_or_equal_bigger_other_less("<=1", "<2")]
586 #[case::self_less_or_equal_bigger_other_less_or_equal("<=1", "<=2")]
587 #[case::self_less_or_equal_smaller_other_greater_or_equal("<=1", ">=0.1")]
588 #[case::self_less_or_equal_smaller_other_greater("<=1", ">0.1")]
589 #[case::self_equal_matching_other_less_or_equal("=1", "<=1")]
590 #[case::self_equal_matching_other_equal("=1", "=1")]
591 #[case::self_equal_matching_other_greater_or_equal("=1", ">=1")]
592 #[case::self_greater_or_equal_matching_other_less_or_equal(">=1", "<=1")]
593 #[case::self_greater_or_equal_matching_other_equal(">=1", "=1")]
594 #[case::self_greater_or_equal_matching_other_greater_or_equal(">=1", ">=1")]
595 #[case::self_greater_or_equal_matching_other_greater(">=1", ">1")]
596 #[case::self_greater_or_equal_bigger_other_less(">=1", "<2")]
597 #[case::self_greater_or_equal_bigger_other_less_or_equal(">=1", "<=2")]
598 #[case::self_greater_or_equal_bigger_other_equal(">=1", "=2")]
599 #[case::self_greater_or_equal_bigger_other_greater_or_equal(">=1", ">=2")]
600 #[case::self_greater_or_equal_bigger_other_greater(">=1", ">2")]
601 #[case::self_greater_or_equal_smaller_other_greater_or_equal(">=1", ">=0.1")]
602 #[case::self_greater_or_equal_smaller_other_greater(">=1", ">0.1")]
603 #[case::self_greater_matching_other_greater_or_equal(">1", ">=1")]
604 #[case::self_greater_matching_other_greater(">1", ">1")]
605 #[case::self_greater_bigger_other_less(">1", "<2")]
606 #[case::self_greater_bigger_other_less_or_equal(">1", "<=2")]
607 #[case::self_greater_bigger_other_equal(">1", "=2")]
608 #[case::self_greater_bigger_other_greater_or_equal(">1", ">=2")]
609 #[case::self_greater_bigger_other_greater(">1", ">2")]
610 #[case::self_greater_smaller_other_greater_or_equal(">1", ">=0.1")]
611 #[case::self_greater_smaller_other_greater(">1", ">0.1")]
612 fn version_requirements_form_intersection(
613 #[case] self_requirement: &str,
614 #[case] other_requirement: &str,
615 ) -> TestResult {
616 let self_requirement: VersionRequirement = self_requirement.parse()?;
617 let other_requirement: VersionRequirement = other_requirement.parse()?;
618
619 assert!(self_requirement.is_intersection(&other_requirement));
620
621 Ok(())
622 }
623
624 #[rstest]
625 #[case::self_less_matching_other_equal("<1", "=1")]
626 #[case::self_less_matching_other_greater_or_equal("<1", ">=1")]
627 #[case::self_less_matching_other_greater("<1", ">1")]
628 #[case::self_less_or_equal_matching_other_greater("<=1", ">1")]
629 #[case::self_equal_matching_other_less("=1", "<1")]
630 #[case::self_equal_matching_other_greater("=1", ">1")]
631 #[case::self_equal_bigger_other_less("=1", "<2")]
632 #[case::self_equal_bigger_other_greater("=1", ">2")]
633 #[case::self_equal_smaller_other_less("=1", "<0.1")]
634 #[case::self_equal_smaller_other_greater("=1", ">0.1")]
635 #[case::self_greater_or_equal_matching_other_less(">=1", "<1")]
636 #[case::self_greater_matching_other_less(">1", "<1")]
637 #[case::self_greater_matching_other_less_or_equal(">1", "<=1")]
638 #[case::self_greater_matching_other_equal(">1", "=1")]
639 fn version_requirements_do_not_form_intersection(
640 #[case] self_requirement: &str,
641 #[case] other_requirement: &str,
642 ) -> TestResult {
643 let self_requirement: VersionRequirement = self_requirement.parse()?;
644 let other_requirement: VersionRequirement = other_requirement.parse()?;
645
646 assert!(!self_requirement.is_intersection(&other_requirement));
647 Ok(())
648 }
649}