1use std::cmp::{Ord, Ordering, PartialOrd};
2use std::convert::TryFrom;
3use std::fmt;
4use std::ops::Deref;
5
6use winnow::ascii::{space0, space1};
7use winnow::combinator::{
8 alt, delimited, eof, opt, peek, preceded, repeat_till, separated, terminated,
9};
10use winnow::token::{any, literal};
11use winnow::{ModalResult, Parser};
12
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize, de::Deserializer, ser::Serializer};
15use smallvec::SmallVec;
16use thiserror::Error;
17
18use crate::{
19 Identifier, MAX_SAFE_INTEGER, SemverError, SemverErrorKind, SemverParseError, Version, extras,
20 number, semver_error_from_parse,
21};
22
23mod fast;
24
25#[cold]
26#[inline(never)]
27fn no_valid_ranges_error(input: &str) -> SemverError {
28 SemverError {
29 input: input.into(),
30 span: (input.len(), 0).into(),
31 kind: SemverErrorKind::NoValidRanges,
32 }
33}
34
35fn should_skip_range_fallback(input: &str) -> bool {
36 let input = input.trim();
37 if starts_with_range_token(input) {
38 return false;
39 }
40
41 is_single_unparseable_token(input)
42}
43
44fn starts_with_range_token(input: &str) -> bool {
45 input
46 .as_bytes()
47 .first()
48 .is_some_and(|ch| is_possible_range_token(*ch))
49}
50
51fn is_possible_range_token(ch: u8) -> bool {
52 matches!(
53 ch,
54 b'0'..=b'9' | b'v' | b'V' | b'x' | b'X' | b'*' | b'>' | b'<' | b'=' | b'^' | b'~'
55 )
56}
57
58fn is_single_unparseable_token(input: &str) -> bool {
59 let mut has_colon = false;
60 let mut has_possible_range_token = false;
61 let mut previous_was_pipe = false;
62
63 for ch in input.bytes() {
64 if is_range_token_separator(ch) {
65 return false;
66 }
67
68 if ch == b':' {
69 has_colon = true;
70 }
71
72 if is_possible_range_token(ch) {
73 has_possible_range_token = true;
74 }
75
76 if ch == b'|' {
77 if previous_was_pipe {
78 return false;
79 }
80 previous_was_pipe = true;
81 } else {
82 previous_was_pipe = false;
83 }
84 }
85
86 has_colon || !has_possible_range_token
87}
88
89fn is_range_token_separator(ch: u8) -> bool {
90 matches!(ch, b' ' | b'\t' | b'\n' | b'\r' | 0x0B | 0x0C)
91}
92
93#[derive(Clone, Debug, Eq, PartialEq, Hash)]
94struct BoundSet {
95 bounds: Box<BoundPair>,
96}
97
98#[derive(Clone, Debug, Eq, PartialEq, Hash)]
99struct BoundPair {
100 upper: Bound,
101 lower: Bound,
102}
103
104impl Deref for BoundSet {
105 type Target = BoundPair;
106
107 fn deref(&self) -> &Self::Target {
108 self.bounds.as_ref()
109 }
110}
111
112impl BoundSet {
113 fn new(lower: Bound, upper: Bound) -> Option<Self> {
114 use Bound::*;
115 use Predicate::*;
116
117 match (lower, upper) {
118 (Lower(Excluding(v1)), Upper(Including(v2)))
119 | (Lower(Including(v1)), Upper(Excluding(v2)))
120 if v1 == v2 =>
121 {
122 None
123 }
124 (Lower(Including(v1)), Upper(Including(v2))) if v1 == v2 => Some(Self {
125 bounds: Box::new(BoundPair {
126 lower: Lower(Including(v1)),
127 upper: Upper(Including(v2)),
128 }),
129 }),
130 (lower, upper) if lower < upper => Some(Self {
131 bounds: Box::new(BoundPair { lower, upper }),
132 }),
133 _ => None,
134 }
135 }
136
137 fn at_least(p: Predicate) -> Option<Self> {
138 BoundSet::new(Bound::Lower(p), Bound::upper())
139 }
140
141 fn at_most(p: Predicate) -> Option<Self> {
142 BoundSet::new(Bound::lower(), Bound::Upper(p))
143 }
144
145 fn exact(version: Version) -> Option<Self> {
146 BoundSet::new(
147 Bound::Lower(Predicate::Including(version.clone())),
148 Bound::Upper(Predicate::Including(version)),
149 )
150 }
151
152 fn satisfies(&self, version: &Version, include_prerelease: bool) -> bool {
153 use Bound::*;
154 use Predicate::*;
155
156 let lower_bound = match &self.lower {
157 Lower(Including(lower)) => lower <= version,
158 Lower(Excluding(lower)) => lower < version,
159 Lower(Unbounded) => true,
160 _ => unreachable!(
161 "There should not have been an upper bound: {:#?}",
162 self.lower
163 ),
164 };
165
166 let upper_bound = match &self.upper {
167 Upper(Including(upper)) => version <= upper,
168 Upper(Excluding(upper)) => version < upper,
169 Upper(Unbounded) => true,
170 _ => unreachable!(
171 "There should not have been an lower bound: {:#?}",
172 self.lower
173 ),
174 };
175
176 if !lower_bound || !upper_bound {
177 return false;
178 }
179
180 if version.is_prerelease() && !include_prerelease {
181 let lower_version = match &self.lower {
182 Lower(Including(v)) => Some(v),
183 Lower(Excluding(v)) => Some(v),
184 _ => None,
185 };
186 if let Some(lower_version) = lower_version {
187 if lower_version.is_prerelease()
188 && version.major == lower_version.major
189 && version.minor == lower_version.minor
190 && version.patch == lower_version.patch
191 {
192 return true;
193 }
194 }
195
196 let upper_version = match &self.upper {
197 Upper(Including(v)) => Some(v),
198 Upper(Excluding(v)) => Some(v),
199 _ => None,
200 };
201 if let Some(upper_version) = upper_version {
202 if upper_version.is_prerelease()
203 && version.major == upper_version.major
204 && version.minor == upper_version.minor
205 && version.patch == upper_version.patch
206 {
207 return true;
208 }
209 }
210
211 return false;
212 }
213
214 true
215 }
216
217 fn allows_all(&self, other: &BoundSet) -> bool {
218 self.lower <= other.lower && other.upper <= self.upper
219 }
220
221 fn allows_any(&self, other: &BoundSet) -> bool {
222 if other.upper < self.lower {
223 return false;
224 }
225
226 if self.upper < other.lower {
227 return false;
228 }
229
230 true
231 }
232
233 fn intersect(&self, other: &Self) -> Option<Self> {
234 let lower: &Bound = std::cmp::max(&self.lower, &other.lower);
235 let upper: &Bound = std::cmp::min(&self.upper, &other.upper);
236
237 BoundSet::new(lower.clone(), upper.clone())
238 }
239
240 fn difference(&self, other: &Self) -> Option<Vec<Self>> {
241 use Bound::*;
242
243 if let Some(overlap) = self.intersect(other) {
244 if &overlap == self {
245 return None;
246 }
247
248 if self.lower < overlap.lower && overlap.upper < self.upper {
249 return Some(vec![
250 BoundSet::new(
251 self.lower.clone(),
252 Upper(overlap.lower.clone().predicate().flip()),
253 )
254 .unwrap(),
255 BoundSet::new(
256 Lower(overlap.upper.clone().predicate().flip()),
257 self.upper.clone(),
258 )
259 .unwrap(),
260 ]);
261 }
262
263 if self.lower < overlap.lower {
264 return BoundSet::new(
265 self.lower.clone(),
266 Upper(overlap.lower.clone().predicate().flip()),
267 )
268 .map(|f| vec![f]);
269 }
270
271 BoundSet::new(
272 Lower(overlap.upper.clone().predicate().flip()),
273 self.upper.clone(),
274 )
275 .map(|f| vec![f])
276 } else {
277 Some(vec![self.clone()])
278 }
279 }
280
281 fn comparators(&self) -> impl Iterator<Item = Comparator> + '_ {
282 ComparatorIter::new(self)
283 }
284}
285
286impl fmt::Display for BoundSet {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 use Bound::*;
289 use Predicate::*;
290 match (&self.lower, &self.upper) {
291 (Lower(Unbounded), Upper(Unbounded)) => write!(f, "*"),
292 (Lower(Unbounded), Upper(Including(v))) => write!(f, "<={}", v),
293 (Lower(Unbounded), Upper(Excluding(v))) => write!(f, "<{}", v),
294 (Lower(Including(v)), Upper(Unbounded)) => write!(f, ">={}", v),
295 (Lower(Excluding(v)), Upper(Unbounded)) => write!(f, ">{}", v),
296 (Lower(Including(v)), Upper(Including(v2))) if v == v2 => write!(f, "{}", v),
297 (Lower(Including(v)), Upper(Including(v2))) => write!(f, ">={} <={}", v, v2),
298 (Lower(Including(v)), Upper(Excluding(v2))) => write!(f, ">={} <{}", v, v2),
299 (Lower(Excluding(v)), Upper(Including(v2))) => write!(f, ">{} <={}", v, v2),
300 (Lower(Excluding(v)), Upper(Excluding(v2))) => write!(f, ">{} <{}", v, v2),
301 _ => unreachable!("does not make sense"),
302 }
303 }
304}
305
306#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
307enum Operation {
308 Exact,
309 GreaterThan,
310 GreaterThanEquals,
311 LessThan,
312 LessThanEquals,
313}
314
315#[derive(Debug, Clone, Eq, PartialEq, Hash)]
316enum Predicate {
317 Excluding(Version), Including(Version), Unbounded, }
321
322impl Predicate {
323 fn flip(self) -> Self {
324 use Predicate::*;
325 match self {
326 Excluding(v) => Including(v),
327 Including(v) => Excluding(v),
328 Unbounded => Unbounded,
329 }
330 }
331}
332
333#[derive(Debug, Clone, Eq, PartialEq, Hash)]
334enum Bound {
335 Lower(Predicate),
336 Upper(Predicate),
337}
338
339impl Bound {
340 fn upper() -> Self {
341 Bound::Upper(Predicate::Unbounded)
342 }
343
344 fn lower() -> Self {
345 Bound::Lower(Predicate::Unbounded)
346 }
347
348 fn predicate(self) -> Predicate {
349 use Bound::*;
350
351 match self {
352 Lower(p) => p,
353 Upper(p) => p,
354 }
355 }
356
357 fn rank(&self) -> (&Version, i8) {
358 use Bound::*;
359 use Predicate::*;
360
361 match self {
362 Upper(Excluding(v)) => (v, 0),
363 Lower(Including(v)) => (v, 1),
364 Upper(Including(v)) => (v, 2),
365 Lower(Excluding(v)) => (v, 3),
366 Lower(Unbounded) | Upper(Unbounded) => {
367 unreachable!("cannot rank unbounded bounds")
368 }
369 }
370 }
371}
372
373impl Ord for Bound {
374 fn cmp(&self, other: &Self) -> Ordering {
375 use Bound::*;
376 use Predicate::*;
377
378 match (self, other) {
379 (Lower(Unbounded), Lower(Unbounded)) | (Upper(Unbounded), Upper(Unbounded)) => {
380 Ordering::Equal
381 }
382 (Lower(Unbounded), _) => Ordering::Less,
383 (_, Lower(Unbounded)) => Ordering::Greater,
384 (Upper(Unbounded), _) => Ordering::Greater,
385 (_, Upper(Unbounded)) => Ordering::Less,
386 _ => {
387 let (self_version, self_rank) = self.rank();
388 let (other_version, other_rank) = other.rank();
389
390 match self_version.cmp(other_version) {
391 Ordering::Equal => self_rank.cmp(&other_rank),
392 ord => ord,
393 }
394 }
395 }
396 }
397}
398
399impl PartialOrd for Bound {
400 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
401 Some(self.cmp(other))
402 }
403}
404
405#[derive(Clone, Debug)]
406struct Comparator {
407 op: Operation,
408 version: Version,
409}
410
411struct ComparatorIter {
412 state: ComparatorIterState,
413}
414
415enum ComparatorIterState {
416 Exact(Option<Comparator>),
417 Pair {
418 items: [Option<Comparator>; 2],
419 index: usize,
420 },
421 Fallback(Option<Comparator>),
422 Done,
423}
424
425impl ComparatorIter {
426 fn new(bound_set: &BoundSet) -> Self {
427 match (&bound_set.lower, &bound_set.upper) {
428 (Bound::Lower(Predicate::Including(low)), Bound::Upper(Predicate::Including(high)))
429 if low == high =>
430 {
431 return Self {
432 state: ComparatorIterState::Exact(Some(Comparator {
433 op: Operation::Exact,
434 version: low.clone(),
435 })),
436 };
437 }
438 _ => {}
439 }
440
441 let upper = Comparator::from_bound(&bound_set.upper);
442 let lower = Comparator::from_bound(&bound_set.lower);
443
444 if upper.is_none() && lower.is_none() {
445 return Self {
446 state: ComparatorIterState::Fallback(Some(Comparator {
447 op: Operation::GreaterThanEquals,
448 version: Version::from((0, 0, 0)),
449 })),
450 };
451 }
452
453 Self {
454 state: ComparatorIterState::Pair {
455 items: [upper, lower],
456 index: 0,
457 },
458 }
459 }
460}
461
462impl Iterator for ComparatorIter {
463 type Item = Comparator;
464
465 fn next(&mut self) -> Option<Self::Item> {
466 use ComparatorIterState::*;
467
468 match &mut self.state {
469 Exact(opt) => {
470 let next = opt.take();
471 if next.is_none() {
472 self.state = Done;
473 }
474 next
475 }
476 Pair { items, index } => {
477 while *index < items.len() {
478 let candidate = items[*index].take();
479 *index += 1;
480 if candidate.is_some() {
481 return candidate;
482 }
483 }
484 self.state = Done;
485 None
486 }
487 Fallback(opt) => {
488 let next = opt.take();
489 if next.is_none() {
490 self.state = Done;
491 }
492 next
493 }
494 Done => None,
495 }
496 }
497}
498
499impl Comparator {
500 fn from_bound(bound: &Bound) -> Option<Self> {
501 match bound {
502 Bound::Lower(Predicate::Including(v)) => Some(Self {
503 op: Operation::GreaterThanEquals,
504 version: v.clone(),
505 }),
506 Bound::Lower(Predicate::Excluding(v)) => Some(Self {
507 op: Operation::GreaterThan,
508 version: v.clone(),
509 }),
510 Bound::Upper(Predicate::Including(v)) => Some(Self {
511 op: Operation::LessThanEquals,
512 version: v.clone(),
513 }),
514 Bound::Upper(Predicate::Excluding(v)) => Some(Self {
515 op: Operation::LessThan,
516 version: v.clone(),
517 }),
518 Bound::Lower(Predicate::Unbounded) | Bound::Upper(Predicate::Unbounded) => None,
519 }
520 }
521}
522
523#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
525pub enum OutsideDirection {
526 Higher,
528 Lower,
530}
531
532impl TryFrom<char> for OutsideDirection {
533 type Error = RangeError;
534
535 fn try_from(value: char) -> Result<Self, Self::Error> {
536 match value {
537 '>' => Ok(Self::Higher),
538 '<' => Ok(Self::Lower),
539 other => Err(RangeError::InvalidOutsideDirection(other)),
540 }
541 }
542}
543
544#[derive(Debug, Clone, Copy, Error, Eq, PartialEq)]
546pub enum RangeError {
547 #[error(
548 "outside() only supports checking whether a version is above or below a range, found `{0}`"
549 )]
550 InvalidOutsideDirection(char),
551 #[error("outside() could not determine comparator bounds for this range")]
552 MissingComparatorBounds,
553}
554
555#[derive(Clone, Debug, Eq, PartialEq, Hash)]
564pub struct Range(SmallVec<[BoundSet; 1]>);
565
566impl fmt::Display for OutsideDirection {
567 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
568 match self {
569 OutsideDirection::Higher => write!(f, ">"),
570 OutsideDirection::Lower => write!(f, "<"),
571 }
572 }
573}
574
575impl fmt::Display for Operation {
576 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
577 use Operation::*;
578 match self {
579 Exact => write!(f, ""),
580 GreaterThan => write!(f, ">"),
581 GreaterThanEquals => write!(f, ">="),
582 LessThan => write!(f, "<"),
583 LessThanEquals => write!(f, "<="),
584 }
585 }
586}
587
588#[cfg(feature = "serde")]
589impl Serialize for Range {
590 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
591 s.collect_str(self)
592 }
593}
594
595#[cfg(feature = "serde")]
596impl<'de> Deserialize<'de> for Range {
597 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
598 let s = String::deserialize(d)?;
599 s.parse().map_err(serde::de::Error::custom)
600 }
601}
602
603impl Range {
604 fn from_bound_set(bound_set: BoundSet) -> Self {
605 let mut bound_sets = SmallVec::new();
606 bound_sets.push(bound_set);
607 Self(bound_sets)
608 }
609
610 fn from_bound_sets(bound_sets: Vec<BoundSet>) -> Option<Self> {
611 (!bound_sets.is_empty()).then(|| Self(SmallVec::from_vec(bound_sets)))
612 }
613
614 fn append_bound_sets_to(self, bound_sets: &mut Vec<BoundSet>) {
615 bound_sets.extend(self.0);
616 }
617
618 fn iter(&self) -> std::slice::Iter<'_, BoundSet> {
619 self.0.iter()
620 }
621
622 pub fn parse<S: AsRef<str>>(input: S) -> Result<Self, SemverError> {
626 let mut input = input.as_ref();
627
628 if input.trim().is_empty() {
629 return Ok(Self::any());
630 }
631
632 if input.split("||").all(|part| part.trim().is_empty()) {
633 return Ok(Self::any());
634 }
635
636 if let Some(range) = fast::parse(input) {
637 return Ok(range);
638 }
639
640 if should_skip_range_fallback(input) {
641 return Err(no_valid_ranges_error(input));
642 }
643
644 if let Some(range) = fast::parse_garbage(input) {
645 return Ok(range);
646 }
647
648 match range_set.parse_next(&mut input) {
649 Ok(range) => Ok(range),
650 Err(err) => Err(semver_error_from_parse(input, err)),
651 }
652 }
653
654 pub fn any() -> Self {
658 Self::from_bound_set(BoundSet::new(Bound::lower(), Bound::upper()).unwrap())
659 }
660
661 pub fn satisfies(&self, version: &Version) -> bool {
665 self.satisfies_with_prerelease(version, false)
666 }
667
668 pub fn satisfies_with_prerelease(&self, version: &Version, include_prerelease: bool) -> bool {
675 self.0
676 .iter()
677 .any(|range| range.satisfies(version, include_prerelease))
678 }
679
680 pub fn allows_all(&self, other: &Range) -> bool {
684 for this in self.iter() {
685 for that in other.iter() {
686 if this.allows_all(that) {
687 return true;
688 }
689 }
690 }
691
692 false
693 }
694
695 pub fn allows_any(&self, other: &Range) -> bool {
699 for this in self.iter() {
700 for that in other.iter() {
701 if this.allows_any(that) {
702 return true;
703 }
704 }
705 }
706
707 false
708 }
709
710 pub fn intersect(&self, other: &Self) -> Option<Self> {
714 let mut sets = Vec::new();
715
716 for lefty in self.iter() {
717 for righty in other.iter() {
718 if let Some(set) = lefty.intersect(righty) {
719 sets.push(set)
720 }
721 }
722 }
723
724 Self::from_bound_sets(sets)
725 }
726
727 pub fn difference(&self, other: &Self) -> Option<Self> {
731 let mut predicates = Vec::new();
732
733 for lefty in self.iter() {
734 for righty in other.iter() {
735 if let Some(mut range) = lefty.difference(righty) {
736 predicates.append(&mut range)
737 }
738 }
739 }
740
741 Self::from_bound_sets(predicates)
742 }
743
744 #[doc = include_str!("../examples/max_satisfying.rs")]
749 pub fn max_satisfying<'v>(&self, versions: &'v [Version]) -> Option<&'v Version> {
751 versions.iter().filter(|v| self.satisfies(v)).max()
752 }
753
754 #[doc = include_str!("../examples/min_satisfying.rs")]
759 pub fn min_satisfying<'v>(&self, versions: &'v [Version]) -> Option<&'v Version> {
761 versions.iter().filter(|v| self.satisfies(v)).min()
762 }
763
764 pub fn min_version(&self) -> Option<Version> {
768 if let Some(min_bound) = self.iter().map(|range| &range.lower).min() {
769 match min_bound {
770 Bound::Lower(pred) => match pred {
771 Predicate::Including(v) => Some(v.clone()),
772 Predicate::Excluding(v) => {
773 let mut v = v.clone();
774 if v.is_prerelease() {
775 v.push_pre_release(Identifier::Numeric(0))
776 } else {
777 v.patch += 1;
778 }
779 Some(v)
780 }
781 Predicate::Unbounded => {
782 let mut zero = Version::from((0, 0, 0));
783 if self.satisfies(&zero) {
784 return Some(zero);
785 }
786
787 zero.push_pre_release(Identifier::Numeric(0));
788 if self.satisfies(&zero) {
789 return Some(zero);
790 }
791 None
792 }
793 },
794 Bound::Upper(_) => None,
795 }
796 } else {
797 None
798 }
799 }
800
801 #[inline]
802 fn outside_higher(
803 &self,
804 version: &Version,
805 include_prerelease: bool,
806 ) -> Result<bool, RangeError> {
807 if self.satisfies_with_prerelease(version, include_prerelease) {
808 return Ok(false);
809 }
810
811 for range in self.iter() {
812 let mut high: Option<Comparator> = None;
813 let mut low: Option<Comparator> = None;
814
815 for comparator in range.comparators() {
816 if high.as_ref().is_none_or(|h| comparator.version > h.version) {
817 high = Some(comparator.clone());
818 }
819
820 if low.as_ref().is_none_or(|l| comparator.version < l.version) {
821 low = Some(comparator);
822 }
823 }
824
825 let (Some(high), Some(low)) = (high.as_ref(), low.as_ref()) else {
826 return Err(RangeError::MissingComparatorBounds);
827 };
828
829 if matches!(
830 high.op,
831 Operation::GreaterThan | Operation::GreaterThanEquals
832 ) {
833 return Ok(false);
834 }
835
836 let low_is_empty = matches!(low.op, Operation::Exact);
837
838 if ((low_is_empty || matches!(low.op, Operation::GreaterThan))
839 && version <= &low.version)
840 || (matches!(low.op, Operation::GreaterThanEquals) && version < &low.version)
841 {
842 return Ok(false);
843 }
844 }
845
846 Ok(true)
847 }
848
849 #[inline]
850 fn outside_lower(
851 &self,
852 version: &Version,
853 include_prerelease: bool,
854 ) -> Result<bool, RangeError> {
855 if self.satisfies_with_prerelease(version, include_prerelease) {
856 return Ok(false);
857 }
858
859 for range in self.iter() {
860 let mut high: Option<Comparator> = None;
861 let mut low: Option<Comparator> = None;
862
863 for comparator in range.comparators() {
864 if high.as_ref().is_none_or(|h| comparator.version < h.version) {
865 high = Some(comparator.clone());
866 }
867
868 if low.as_ref().is_none_or(|l| comparator.version > l.version) {
869 low = Some(comparator);
870 }
871 }
872
873 let (Some(high), Some(low)) = (high.as_ref(), low.as_ref()) else {
874 return Err(RangeError::MissingComparatorBounds);
875 };
876
877 if matches!(high.op, Operation::LessThan | Operation::LessThanEquals) {
878 return Ok(false);
879 }
880
881 let low_is_empty = matches!(low.op, Operation::Exact);
882
883 if ((low_is_empty || matches!(low.op, Operation::LessThan)) && version >= &low.version)
884 || (matches!(low.op, Operation::LessThanEquals) && version > &low.version)
885 {
886 return Ok(false);
887 }
888 }
889
890 Ok(true)
891 }
892
893 pub fn outside(
906 &self,
907 version: &Version,
908 direction: OutsideDirection,
909 include_prerelease: bool,
910 ) -> Result<bool, RangeError> {
911 match direction {
912 OutsideDirection::Higher => self.outside_higher(version, include_prerelease),
913 OutsideDirection::Lower => self.outside_lower(version, include_prerelease),
914 }
915 }
916}
917
918impl fmt::Display for Range {
919 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
920 for (i, range) in self.iter().enumerate() {
921 if i > 0 {
922 write!(f, "||")?;
923 }
924 write!(f, "{}", range)?;
925 }
926 Ok(())
927 }
928}
929
930impl std::str::FromStr for Range {
931 type Err = SemverError;
932 fn from_str(s: &str) -> Result<Self, Self::Err> {
933 Range::parse(s)
934 }
935}
936
937fn range_set<'s>(input: &mut &'s str) -> ModalResult<Range, SemverParseError<&'s str>> {
976 Parser::try_map(bound_sets, |sets| {
977 if sets.is_empty() {
978 Err(SemverParseError {
979 input,
980 kind: Some(SemverErrorKind::NoValidRanges),
981 context: None,
982 })
983 } else {
984 Ok(Range::from_bound_sets(sets).expect("non-empty bound sets"))
985 }
986 })
987 .parse_next(input)
988}
989
990fn bound_sets<'s>(input: &mut &'s str) -> ModalResult<Vec<BoundSet>, SemverParseError<&'s str>> {
992 Parser::map(
993 separated(0.., range, logical_or),
994 |sets: Vec<Vec<BoundSet>>| sets.into_iter().flatten().collect(),
995 )
996 .parse_next(input)
997}
998fn logical_or<'s>(input: &mut &'s str) -> ModalResult<(), SemverParseError<&'s str>> {
999 Parser::map(delimited(space0, literal("||"), space0), |_| ()).parse_next(input)
1000}
1001
1002fn range<'s>(input: &mut &'s str) -> ModalResult<Vec<BoundSet>, SemverParseError<&'s str>> {
1003 Parser::map(
1006 separated(0.., simple, space1),
1007 |bs: Vec<Option<BoundSet>>| {
1008 bs.into_iter()
1009 .flatten()
1010 .fold(Vec::new(), |mut acc: Vec<BoundSet>, bs| {
1011 if let Some(last) = acc.pop() {
1012 if let Some(bound) = last.intersect(&bs) {
1013 acc.push(bound);
1014 } else {
1015 acc.push(last);
1016 acc.push(bs);
1017 }
1018 } else {
1019 acc.push(bs)
1020 }
1021 acc
1022 })
1023 },
1024 )
1025 .parse_next(input)
1026}
1027
1028fn simple<'s>(input: &mut &'s str) -> ModalResult<Option<BoundSet>, SemverParseError<&'s str>> {
1030 alt((
1031 terminated(hyphen, peek(alt((space1, literal("||"), eof)))),
1032 terminated(primitive, peek(alt((space1, literal("||"), eof)))),
1033 terminated(partial, peek(alt((space1, literal("||"), eof)))),
1034 terminated(tilde, peek(alt((space1, literal("||"), eof)))),
1035 terminated(caret, peek(alt((space1, literal("||"), eof)))),
1036 garbage,
1037 ))
1038 .parse_next(input)
1039}
1040
1041fn garbage<'s>(input: &mut &'s str) -> ModalResult<Option<BoundSet>, SemverParseError<&'s str>> {
1042 Parser::map(
1043 repeat_till(0.., any, alt((peek(space1), peek(literal("||")), eof))),
1044 |_: ((), &str)| None,
1045 )
1046 .parse_next(input)
1047}
1048
1049fn primitive<'s>(input: &mut &'s str) -> ModalResult<Option<BoundSet>, SemverParseError<&'s str>> {
1051 use Operation::*;
1052
1053 Parser::map(
1054 (operation, preceded(space0, partial_version)),
1055 |parsed| match parsed {
1056 (GreaterThanEquals, partial) => {
1057 BoundSet::at_least(Predicate::Including(partial.into()))
1058 }
1059 (
1060 GreaterThan,
1061 Partial {
1062 major: Some(major),
1063 minor: Some(minor),
1064 patch: None,
1065 ..
1066 },
1067 ) => BoundSet::at_least(Predicate::Including((major, minor + 1, 0).into())),
1068 (
1069 GreaterThan,
1070 Partial {
1071 major: Some(major),
1072 minor: None,
1073 patch: None,
1074 ..
1075 },
1076 ) => BoundSet::at_least(Predicate::Including((major + 1, 0, 0).into())),
1077 (GreaterThan, partial) => BoundSet::at_least(Predicate::Excluding(partial.into())),
1078 (
1079 LessThan,
1080 Partial {
1081 major: Some(major),
1082 minor: Some(minor),
1083 patch: None,
1084 ..
1085 },
1086 ) => BoundSet::at_most(Predicate::Excluding((major, minor, 0, 0).into())),
1087 (
1088 LessThan,
1089 Partial {
1090 major,
1091 minor,
1092 patch,
1093 pre_release,
1094 build,
1095 ..
1096 },
1097 ) => BoundSet::at_most(Predicate::Excluding(Version::new(
1098 major.unwrap_or(0),
1099 minor.unwrap_or(0),
1100 patch.unwrap_or(0),
1101 pre_release,
1102 build,
1103 ))),
1104 (
1105 LessThanEquals,
1106 Partial {
1107 major,
1108 minor: None,
1109 patch: None,
1110 ..
1111 },
1112 ) => BoundSet::at_most(Predicate::Including(
1113 (major.unwrap_or(0), MAX_SAFE_INTEGER, MAX_SAFE_INTEGER).into(),
1114 )),
1115 (
1116 LessThanEquals,
1117 Partial {
1118 major,
1119 minor,
1120 patch: None,
1121 ..
1122 },
1123 ) => BoundSet::at_most(Predicate::Including(
1124 (major.unwrap_or(0), minor.unwrap_or(0), MAX_SAFE_INTEGER).into(),
1125 )),
1126 (LessThanEquals, partial) => BoundSet::at_most(Predicate::Including(partial.into())),
1127 (
1128 Exact,
1129 Partial {
1130 major: Some(major),
1131 minor: Some(minor),
1132 patch: Some(patch),
1133 pre_release,
1134 ..
1135 },
1136 ) => BoundSet::exact(Version::new(major, minor, patch, pre_release, vec![])),
1137 (
1138 Exact,
1139 Partial {
1140 major: Some(major),
1141 minor: Some(minor),
1142 ..
1143 },
1144 ) => BoundSet::new(
1145 Bound::Lower(Predicate::Including((major, minor, 0).into())),
1146 Bound::Upper(Predicate::Excluding((major, minor + 1, 0, 0).into())),
1147 ),
1148 (
1149 Exact,
1150 Partial {
1151 major: Some(major), ..
1152 },
1153 ) => BoundSet::new(
1154 Bound::Lower(Predicate::Including((major, 0, 0).into())),
1155 Bound::Upper(Predicate::Excluding((major + 1, 0, 0, 0).into())),
1156 ),
1157 _ => None,
1158 },
1159 )
1160 .context("operation range (ex: >= 1.2.3)")
1161 .parse_next(input)
1162}
1163
1164fn operation<'s>(input: &mut &'s str) -> ModalResult<Operation, SemverParseError<&'s str>> {
1165 use Operation::*;
1166 alt((
1167 Parser::map(literal(">="), |_| GreaterThanEquals),
1168 Parser::map(literal(">"), |_| GreaterThan),
1169 Parser::map(literal("="), |_| Exact),
1170 Parser::map(literal("<="), |_| LessThanEquals),
1171 Parser::map(literal("<"), |_| LessThan),
1172 ))
1173 .parse_next(input)
1174}
1175
1176fn partial<'s>(input: &mut &'s str) -> ModalResult<Option<BoundSet>, SemverParseError<&'s str>> {
1177 Parser::map(partial_version, |partial| match partial {
1178 Partial { major: None, .. } => BoundSet::at_least(Predicate::Including((0, 0, 0).into())),
1179 Partial {
1180 major: Some(major),
1181 minor: None,
1182 ..
1183 } => BoundSet::new(
1184 Bound::Lower(Predicate::Including((major, 0, 0).into())),
1185 Bound::Upper(Predicate::Excluding((major + 1, 0, 0, 0).into())),
1186 ),
1187 Partial {
1188 major: Some(major),
1189 minor: Some(minor),
1190 patch: None,
1191 ..
1192 } => BoundSet::new(
1193 Bound::Lower(Predicate::Including((major, minor, 0).into())),
1194 Bound::Upper(Predicate::Excluding((major, minor + 1, 0, 0).into())),
1195 ),
1196 partial => BoundSet::exact(partial.into()),
1197 })
1198 .context("plain version range (ex: 1.2)")
1199 .parse_next(input)
1200}
1201
1202#[derive(Debug, Clone)]
1203struct Partial {
1204 major: Option<u64>,
1205 minor: Option<u64>,
1206 patch: Option<u64>,
1207 pre_release: Vec<Identifier>,
1208 build: Vec<Identifier>,
1209}
1210
1211impl From<Partial> for Version {
1212 fn from(partial: Partial) -> Self {
1213 Version::new(
1214 partial.major.unwrap_or(0),
1215 partial.minor.unwrap_or(0),
1216 partial.patch.unwrap_or(0),
1217 partial.pre_release,
1218 partial.build,
1219 )
1220 }
1221}
1222
1223fn partial_version<'s>(input: &mut &'s str) -> ModalResult<Partial, SemverParseError<&'s str>> {
1228 let _ = opt(literal("v")).parse_next(input)?;
1229 let _ = space0(input)?;
1230 let major = component(input)?;
1231 let minor = opt(preceded(literal("."), component)).parse_next(input)?;
1232 let patch = opt(preceded(literal("."), component)).parse_next(input)?;
1233 let (pre, build) = if patch.is_some() {
1234 extras(input)?
1235 } else {
1236 (vec![], vec![])
1237 };
1238 Ok(Partial {
1239 major,
1240 minor: minor.flatten(),
1241 patch: patch.flatten(),
1242 pre_release: pre,
1243 build,
1244 })
1245}
1246
1247fn component<'s>(input: &mut &'s str) -> ModalResult<Option<u64>, SemverParseError<&'s str>> {
1248 alt((
1249 Parser::map(x_or_asterisk, |_| None),
1250 Parser::map(number, Some),
1251 ))
1252 .parse_next(input)
1253}
1254
1255fn x_or_asterisk<'s>(input: &mut &'s str) -> ModalResult<(), SemverParseError<&'s str>> {
1256 Parser::map(alt((literal("x"), literal("X"), literal("*"))), |_| ()).parse_next(input)
1257}
1258
1259fn tilde_gt<'s>(input: &mut &'s str) -> ModalResult<Option<&'s str>, SemverParseError<&'s str>> {
1260 Parser::map(
1261 (literal("~"), space0, opt(literal(">")), space0),
1262 |(_, _, gt, _)| gt,
1263 )
1264 .parse_next(input)
1265}
1266
1267fn tilde<'s>(input: &mut &'s str) -> ModalResult<Option<BoundSet>, SemverParseError<&'s str>> {
1268 Parser::map((tilde_gt, partial_version), |parsed| match parsed {
1269 (
1270 Some(_gt),
1271 Partial {
1272 major: Some(major),
1273 minor: None,
1274 patch: None,
1275 ..
1276 },
1277 ) => BoundSet::new(
1278 Bound::Lower(Predicate::Including((major, 0, 0).into())),
1279 Bound::Upper(Predicate::Excluding((major + 1, 0, 0, 0).into())),
1280 ),
1281 (
1282 Some(_gt),
1283 Partial {
1284 major: Some(major),
1285 minor: Some(minor),
1286 patch,
1287 pre_release,
1288 ..
1289 },
1290 ) => BoundSet::new(
1291 Bound::Lower(Predicate::Including(Version::new(
1292 major,
1293 minor,
1294 patch.unwrap_or(0),
1295 pre_release,
1296 vec![],
1297 ))),
1298 Bound::Upper(Predicate::Excluding((major, minor + 1, 0, 0).into())),
1299 ),
1300 (
1301 None,
1302 Partial {
1303 major: Some(major),
1304 minor: Some(minor),
1305 patch: Some(patch),
1306 pre_release,
1307 ..
1308 },
1309 ) => BoundSet::new(
1310 Bound::Lower(Predicate::Including(Version::new(
1311 major,
1312 minor,
1313 patch,
1314 pre_release,
1315 vec![],
1316 ))),
1317 Bound::Upper(Predicate::Excluding((major, minor + 1, 0, 0).into())),
1318 ),
1319 (
1320 None,
1321 Partial {
1322 major: Some(major),
1323 minor: Some(minor),
1324 patch: None,
1325 ..
1326 },
1327 ) => BoundSet::new(
1328 Bound::Lower(Predicate::Including((major, minor, 0).into())),
1329 Bound::Upper(Predicate::Excluding((major, minor + 1, 0, 0).into())),
1330 ),
1331 (
1332 None,
1333 Partial {
1334 major: Some(major),
1335 minor: None,
1336 patch: None,
1337 ..
1338 },
1339 ) => BoundSet::new(
1340 Bound::Lower(Predicate::Including((major, 0, 0).into())),
1341 Bound::Upper(Predicate::Excluding((major + 1, 0, 0, 0).into())),
1342 ),
1343 _ => None,
1344 })
1345 .context("tilde version range (ex: ~1.2.3)")
1346 .parse_next(input)
1347}
1348
1349fn caret<'s>(input: &mut &'s str) -> ModalResult<Option<BoundSet>, SemverParseError<&'s str>> {
1350 Parser::map(
1351 preceded((literal("^"), space0), partial_version),
1352 |parsed| match parsed {
1353 Partial {
1354 major: Some(0),
1355 minor: None,
1356 patch: None,
1357 ..
1358 } => BoundSet::at_most(Predicate::Excluding((1, 0, 0, 0).into())),
1359 Partial {
1360 major: Some(0),
1361 minor: Some(minor),
1362 patch: None,
1363 ..
1364 } => BoundSet::new(
1365 Bound::Lower(Predicate::Including((0, minor, 0).into())),
1366 Bound::Upper(Predicate::Excluding((0, minor + 1, 0, 0).into())),
1367 ),
1368 Partial {
1370 major: Some(major),
1371 minor: None,
1372 patch: None,
1373 ..
1374 } => BoundSet::new(
1375 Bound::Lower(Predicate::Including((major, 0, 0).into())),
1376 Bound::Upper(Predicate::Excluding((major + 1, 0, 0, 0).into())),
1377 ),
1378 Partial {
1379 major: Some(major),
1380 minor: Some(minor),
1381 patch: None,
1382 ..
1383 } => BoundSet::new(
1384 Bound::Lower(Predicate::Including((major, minor, 0).into())),
1385 Bound::Upper(Predicate::Excluding((major + 1, 0, 0, 0).into())),
1386 ),
1387 Partial {
1388 major: Some(major),
1389 minor: Some(minor),
1390 patch: Some(patch),
1391 pre_release,
1392 ..
1393 } => BoundSet::new(
1394 Bound::Lower(Predicate::Including(Version::new(
1395 major,
1396 minor,
1397 patch,
1398 pre_release,
1399 vec![],
1400 ))),
1401 Bound::Upper(Predicate::Excluding(match (major, minor, patch) {
1402 (0, 0, n) => Version::from((0, 0, n + 1, 0)),
1403 (0, n, _) => Version::from((0, n + 1, 0, 0)),
1404 (n, _, _) => Version::from((n + 1, 0, 0, 0)),
1405 })),
1406 ),
1407 _ => None,
1408 },
1409 )
1410 .context("caret version range (ex: ^1.2.3)")
1411 .parse_next(input)
1412}
1413
1414fn hyphen<'s>(input: &mut &'s str) -> ModalResult<Option<BoundSet>, SemverParseError<&'s str>> {
1416 fn parser<'s>(input: &mut &'s str) -> ModalResult<Option<BoundSet>, SemverParseError<&'s str>> {
1417 let lower = opt(partial_version).parse_next(input)?;
1418 let _ = space1(input)?;
1419 let _ = literal("-").parse_next(input)?;
1420 let _ = space1(input)?;
1421 let upper = partial_version(input)?;
1422 let upper = match upper {
1423 Partial {
1424 major: None,
1425 minor: None,
1426 patch: None,
1427 ..
1428 } => Predicate::Excluding((0, 0, 0, 0).into()),
1429 Partial {
1430 major: Some(major),
1431 minor: None,
1432 patch: None,
1433 ..
1434 } => Predicate::Excluding((major + 1, 0, 0, 0).into()),
1435 Partial {
1436 major: Some(major),
1437 minor: Some(minor),
1438 patch: None,
1439 ..
1440 } => Predicate::Excluding((major, minor + 1, 0, 0).into()),
1441 partial => Predicate::Including(partial.into()),
1442 };
1443 let bounds = if let Some(lower) = lower {
1444 BoundSet::new(
1445 Bound::Lower(Predicate::Including(lower.into())),
1446 Bound::Upper(upper),
1447 )
1448 } else {
1449 BoundSet::at_most(upper)
1450 };
1451 Ok(bounds)
1452 }
1453
1454 parser
1455 .context("hyphenated version range (ex: 1.2 - 2)")
1456 .parse_next(input)
1457}
1458
1459macro_rules! create_tests_for {
1460 ($func:ident $($name:ident => $version_range:expr , { $x:ident => $allows:expr, $y:ident => $denies:expr$(,)? }),+ ,$(,)?) => {
1461
1462 #[cfg(test)]
1463 mod $func {
1464 use super::*;
1465
1466 $(
1467 #[test]
1468 fn $name() {
1469 let version_range = Range::parse($version_range).unwrap();
1470
1471 let allows: Vec<Range> = $allows.iter().map(|v| Range::parse(v).unwrap()).collect();
1472 for version in &allows {
1473 assert!(version_range.$func(version), "should have allowed: {}", version);
1474 }
1475
1476 let ranges: Vec<Range> = $denies.iter().map(|v| Range::parse(v).unwrap()).collect();
1477 for version in &ranges {
1478 assert!(!version_range.$func(version), "should have denied: {}", version);
1479 }
1480 }
1481 )+
1482 }
1483 }
1484}
1485
1486create_tests_for! {
1487 allows_all
1489
1490 greater_than_eq_123 => ">=1.2.3", {
1491 allows => [">=2.0.0", ">2", "2.0.0", "0.1 || 1.4", "1.2.3", "2 - 7", ">2.0.0"],
1492 denies => ["1.0.0", "<1.2", ">=1.2.2", "1 - 3", "0.1 || <1.2.0", ">1.0.0"],
1493 },
1494
1495 greater_than_123 => ">1.2.3", {
1496 allows => [">=2.0.0", ">2", "2.0.0", "0.1 || 1.4", ">2.0.0"],
1497 denies => ["1.0.0", "<1.2", ">=1.2.3", "1 - 3", "0.1 || <1.2.0", "<=3"],
1498 },
1499
1500 eq_123 => "1.2.3", {
1501 allows => ["1.2.3"],
1502 denies => ["1.0.0", "<1.2", "1.x", ">=1.2.2", "1 - 3", "0.1 || <1.2.0"],
1503 },
1504
1505 lt_123 => "<1.2.3", {
1506 allows => ["<=1.2.0", "<1", "1.0.0", "0.1 || 1.4"],
1507 denies => ["1 - 3", ">1", "2.0.0", "2.0 || >9", ">1.0.0"],
1508 },
1509
1510 lt_eq_123 => "<=1.2.3", {
1511 allows => ["<=1.2.0", "<1", "1.0.0", "0.1 || 1.4", "1.2.3"],
1512 denies => ["1 - 3", ">1.0.0", ">=1.0.0"],
1513 },
1514
1515 eq_123_or_gt_400 => "1.2.3 || >4", {
1516 allows => [ "1.2.3", ">4", "5.x", "5.2.x", ">=8.2.1", "2.0 || 5.6.7"],
1517 denies => ["<2", "1 - 7", "1.9.4 || 2 - 3"],
1518 },
1519
1520 between_two_and_eight => "2 - 8", {
1521 allows => [ "2.2.3", "4 - 5"],
1522 denies => ["1 - 4", "5 - 9", ">3", "<=5"],
1523 },
1524}
1525
1526create_tests_for! {
1527 allows_any
1529
1530 greater_than_eq_123 => ">=1.2.3", {
1531 allows => ["<=1.2.4", "3.0.0", "<2", ">=3", ">3.0.0"],
1532 denies => ["<=1.2.0", "1.0.0", "<1"],
1533 },
1534
1535 greater_than_123 => ">1.2.3", {
1536 allows => ["<=1.2.4", "3.0.0", "<2", ">=3", ">3.0.0"],
1537 denies => ["<=1.2.3", "1.0.0", "<1"],
1538 },
1539
1540 eq_123 => "1.2.3", {
1541 allows => ["1.2.3", "1 - 2"],
1542 denies => ["<1.2.3", "1.0.0", ">4.5.6", ">5"],
1543 },
1544
1545 lt_eq_123 => "<=1.2.3", {
1546 allows => ["<=1.2.0", "<1.0.0", "1.0.0", ">1.0.0", ">=1.2.0"],
1547 denies => ["4.5.6", ">2.0.0", ">=2.0.0"],
1548 },
1549
1550 lt_123 => "<1.2.3", {
1551 allows => ["<=2.2.0", "<2.0.0", "1.0.0", ">1.0.0", ">=1.2.0"],
1552 denies => ["2.0.0", ">1.8.0", ">=1.8.0"],
1553 },
1554
1555 between_two_and_eight => "2 - 8", {
1556 allows => ["2.2.3", "4 - 10", ">4", ">4.0.0", "<=4.0.0", "<9.1.2"],
1557 denies => [">10", "10 - 11", "0 - 1"],
1558 },
1559
1560 eq_123_or_gt_400 => "1.2.3 || >4", {
1561 allows => [ "1.2.3", ">3", "5.x", "5.2.x", ">=8.2.1", "2 - 7", "2.0 || 5.6.7"],
1562 denies => [ "1.9.4 || 2 - 3"],
1563 },
1564}
1565
1566#[cfg(test)]
1567mod intersection {
1568 use super::*;
1569
1570 fn v(range: &'static str) -> Range {
1571 range.parse().unwrap()
1572 }
1573
1574 #[test]
1575 fn gt_eq_123() {
1576 let base_range = v(">=1.2.3");
1577
1578 let samples = vec![
1579 ("<=2.0.0", Some(">=1.2.3 <=2.0.0")),
1580 ("<2.0.0", Some(">=1.2.3 <2.0.0")),
1581 (">=2.0.0", Some(">=2.0.0")),
1582 (">2.0.0", Some(">2.0.0")),
1583 (">1.0.0", Some(">=1.2.3")),
1584 (">1.2.3", Some(">1.2.3")),
1585 ("<=1.2.3", Some("1.2.3")),
1586 ("2.0.0", Some("2.0.0")),
1587 ("1.1.1", None),
1588 ("<1.0.0", None),
1589 ];
1590
1591 assert_ranges_match(base_range, samples);
1592 }
1593
1594 #[test]
1595 fn gt_123() {
1596 let base_range = v(">1.2.3");
1597
1598 let samples = vec![
1599 ("<=2.0.0", Some(">1.2.3 <=2.0.0")),
1600 ("<2.0.0", Some(">1.2.3 <2.0.0")),
1601 (">=2.0.0", Some(">=2.0.0")),
1602 (">2.0.0", Some(">2.0.0")),
1603 ("2.0.0", Some("2.0.0")),
1604 (">1.2.3", Some(">1.2.3")),
1605 ("<=1.2.3", None),
1606 ("1.1.1", None),
1607 ("<1.0.0", None),
1608 ];
1609
1610 assert_ranges_match(base_range, samples);
1611 }
1612
1613 #[test]
1614 fn eq_123() {
1615 let base_range = v("1.2.3");
1616
1617 let samples = vec![
1618 ("<=2.0.0", Some("1.2.3")),
1619 ("<2.0.0", Some("1.2.3")),
1620 (">=2.0.0", None),
1621 (">2.0.0", None),
1622 ("2.0.0", None),
1623 ("1.2.3", Some("1.2.3")),
1624 (">1.2.3", None),
1625 ("<=1.2.3", Some("1.2.3")),
1626 ("1.1.1", None),
1627 ("<1.0.0", None),
1628 ];
1629
1630 assert_ranges_match(base_range, samples);
1631 }
1632
1633 #[test]
1634 fn lt_123() {
1635 let base_range = v("<1.2.3");
1636
1637 let samples = vec![
1638 ("<=2.0.0", Some("<1.2.3")),
1639 ("<2.0.0", Some("<1.2.3")),
1640 (">=2.0.0", None),
1641 (">=1.0.0", Some(">=1.0.0 <1.2.3")),
1642 (">2.0.0", None),
1643 ("2.0.0", None),
1644 ("1.2.3", None),
1645 (">1.2.3", None),
1646 ("<=1.2.3", Some("<1.2.3")),
1647 ("1.1.1", Some("1.1.1")),
1648 ("<1.0.0", Some("<1.0.0")),
1649 ];
1650
1651 assert_ranges_match(base_range, samples);
1652 }
1653
1654 #[test]
1655 fn lt_eq_123() {
1656 let base_range = v("<=1.2.3");
1657
1658 let samples = vec![
1659 ("<=2.0.0", Some("<=1.2.3")),
1660 ("<2.0.0", Some("<=1.2.3")),
1661 (">=2.0.0", None),
1662 (">=1.0.0", Some(">=1.0.0 <=1.2.3")),
1663 (">2.0.0", None),
1664 ("2.0.0", None),
1665 ("1.2.3", Some("1.2.3")),
1666 (">1.2.3", None),
1667 ("<=1.2.3", Some("<=1.2.3")),
1668 ("1.1.1", Some("1.1.1")),
1669 ("<1.0.0", Some("<1.0.0")),
1670 ];
1671
1672 assert_ranges_match(base_range, samples);
1673 }
1674
1675 #[test]
1676 fn multiple() {
1677 let base_range = v("<1 || 3 - 4");
1678
1679 let samples = vec![("0.5 - 3.5.0", Some(">=0.5.0 <1.0.0||>=3.0.0 <=3.5.0"))];
1680
1681 assert_ranges_match(base_range, samples);
1682 }
1683
1684 fn assert_ranges_match(base: Range, samples: Vec<(&'static str, Option<&'static str>)>) {
1685 for (other, expected) in samples {
1686 let other = v(other);
1687 let resulting_range = base.intersect(&other).map(|v| v.to_string());
1688 assert_eq!(
1689 resulting_range.clone(),
1690 expected.map(|e| e.to_string()),
1691 "{} ∩ {} := {}",
1692 base,
1693 other,
1694 resulting_range.unwrap_or_else(|| "⊗".into())
1695 );
1696 }
1697 }
1698}
1699
1700#[cfg(test)]
1701mod difference {
1702 use super::*;
1703
1704 fn v(range: &'static str) -> Range {
1705 range.parse().unwrap()
1706 }
1707
1708 #[test]
1709 fn gt_eq_123() {
1710 let base_range = v(">=1.2.3");
1711
1712 let samples = vec![
1713 ("<=2.0.0", Some(">2.0.0")),
1714 ("<2.0.0", Some(">=2.0.0")),
1715 (">=2.0.0", Some(">=1.2.3 <2.0.0")),
1716 (">2.0.0", Some(">=1.2.3 <=2.0.0")),
1717 (">1.0.0", None),
1718 (">1.2.3", Some("1.2.3")),
1719 ("<=1.2.3", Some(">1.2.3")),
1720 ("1.1.1", Some(">=1.2.3")),
1721 ("<1.0.0", Some(">=1.2.3")),
1722 ("2.0.0", Some(">=1.2.3 <2.0.0||>2.0.0")),
1723 ];
1724
1725 assert_ranges_match(base_range, samples);
1726 }
1727
1728 #[test]
1729 fn gt_123() {
1730 let base_range = v(">1.2.3");
1731
1732 let samples = vec![
1733 ("<=2.0.0", Some(">2.0.0")),
1734 ("<2.0.0", Some(">=2.0.0")),
1735 (">=2.0.0", Some(">1.2.3 <2.0.0")),
1736 (">2.0.0", Some(">1.2.3 <=2.0.0")),
1737 (">1.0.0", None),
1738 (">1.2.3", None),
1739 ("<=1.2.3", Some(">1.2.3")),
1740 ("1.1.1", Some(">1.2.3")),
1741 ("<1.0.0", Some(">1.2.3")),
1742 ("2.0.0", Some(">1.2.3 <2.0.0||>2.0.0")),
1743 ];
1744
1745 assert_ranges_match(base_range, samples);
1746 }
1747
1748 #[test]
1749 fn eq_123() {
1750 let base_range = v("1.2.3");
1751
1752 let samples = vec![
1753 ("<=2.0.0", None),
1754 ("<2.0.0", None),
1755 (">=2.0.0", Some("1.2.3")),
1756 (">2.0.0", Some("1.2.3")),
1757 (">1.0.0", None),
1758 (">1.2.3", Some("1.2.3")),
1759 ("1.2.3", None),
1760 ("<=1.2.3", None),
1761 ("1.1.1", Some("1.2.3")),
1762 ("<1.0.0", Some("1.2.3")),
1763 ("2.0.0", Some("1.2.3")),
1764 ];
1765
1766 assert_ranges_match(base_range, samples);
1767 }
1768
1769 #[test]
1770 fn lt_123() {
1771 let base_range = v("<1.2.3");
1772
1773 let samples = vec![
1774 ("<=2.0.0", None),
1775 ("<2.0.0", None),
1776 (">=2.0.0", Some("<1.2.3")),
1777 (">2.0.0", Some("<1.2.3")),
1778 (">1.0.0", Some("<=1.0.0")),
1779 (">1.2.3", Some("<1.2.3")),
1780 ("<=1.2.3", None),
1781 ("1.1.1", Some("<1.1.1||>1.1.1 <1.2.3")),
1782 ("<1.0.0", Some(">=1.0.0 <1.2.3")),
1783 ("2.0.0", Some("<1.2.3")),
1784 ];
1785
1786 assert_ranges_match(base_range, samples);
1787 }
1788
1789 #[test]
1790 fn lt_eq_123() {
1791 let base_range = v("<=1.2.3");
1792
1793 let samples = vec![
1794 ("<=2.0.0", None),
1795 ("<2.0.0", None),
1796 (">=2.0.0", Some("<=1.2.3")),
1797 (">2.0.0", Some("<=1.2.3")),
1798 (">1.0.0", Some("<=1.0.0")),
1799 (">1.2.3", Some("<=1.2.3")),
1800 ("<=1.2.3", None),
1801 ("1.1.1", Some("<1.1.1||>1.1.1 <=1.2.3")),
1802 ("<1.0.0", Some(">=1.0.0 <=1.2.3")),
1803 ("2.0.0", Some("<=1.2.3")),
1804 ];
1805
1806 assert_ranges_match(base_range, samples);
1807 }
1808
1809 #[test]
1810 fn multiple() {
1811 let base_range = v("<1 || 3 - 4");
1812
1813 let samples = vec![("0.5 - 3.5.0", Some("<0.5.0||>3.5.0 <5.0.0-0"))];
1814
1815 assert_ranges_match(base_range, samples);
1816 }
1817
1818 fn assert_ranges_match(base: Range, samples: Vec<(&'static str, Option<&'static str>)>) {
1819 for (other, expected) in samples {
1820 let other = v(other);
1821 let resulting_range = base.difference(&other).map(|v| v.to_string());
1822 assert_eq!(
1823 resulting_range.clone(),
1824 expected.map(|e| e.to_string()),
1825 "{} \\ {} := {}",
1826 base,
1827 other,
1828 resulting_range.unwrap_or_else(|| "⊗".into())
1829 );
1830 }
1831 }
1832}
1833
1834#[cfg(test)]
1835mod outside {
1836 use super::*;
1837 use std::convert::TryFrom;
1838
1839 const VERSION_GT_RANGE: &[(&str, &str, bool)] = &[
1840 ("~1.2.2", "1.3.0", false),
1841 ("~0.6.1-1", "0.7.1-1", false),
1842 ("1.0.0 - 2.0.0", "2.0.1", false),
1843 ("1.0.0", "1.0.1-beta1", false),
1844 ("1.0.0", "2.0.0", false),
1845 ("<=2.0.0", "2.1.1", false),
1846 ("<=2.0.0", "3.2.9", false),
1847 ("<2.0.0", "2.0.0", false),
1848 ("0.1.20 || 1.2.4", "1.2.5", false),
1849 ("2.x.x", "3.0.0", false),
1850 ("1.2.x", "1.3.0", false),
1851 ("1.2.x || 2.x", "3.0.0", false),
1852 ("2.*.*", "5.0.1", false),
1853 ("1.2.*", "1.3.3", false),
1854 ("1.2.* || 2.*", "4.0.0", false),
1855 ("2", "3.0.0", false),
1856 ("2.3", "2.4.2", false),
1857 ("~2.4", "2.5.0", false),
1858 ("~2.4", "2.5.5", false),
1859 ("~>3.2.1", "3.3.0", false),
1860 ("~1", "2.2.3", false),
1861 ("~>1", "2.2.4", false),
1862 ("~> 1", "3.2.3", false),
1863 ("~1.0", "1.1.2", false),
1864 ("~ 1.0", "1.1.0", false),
1865 ("<1.2", "1.2.0", false),
1866 ("< 1.2", "1.2.1", false),
1867 ("1", "2.0.0beta", false),
1868 ("~v0.5.4-pre", "0.6.0", false),
1869 ("~v0.5.4-pre", "0.6.1-pre", false),
1870 ("=0.7.x", "0.8.0", false),
1871 ("=0.7.x", "0.8.0-asdf", false),
1872 ("<0.7.x", "0.7.0", false),
1873 ("1.0.0 - 2.0.0", "2.2.3", false),
1874 ("1.0.0", "1.0.1", false),
1875 ("<=2.0.0", "3.0.0", false),
1876 ("<=2.0.0", "2.9999.9999", false),
1877 ("<=2.0.0", "2.2.9", false),
1878 ("<2.0.0", "2.9999.9999", false),
1879 ("<2.0.0", "2.2.9", false),
1880 ("2.x.x", "3.1.3", false),
1881 ("1.2.x", "1.3.3", false),
1882 ("1.2.x || 2.x", "3.1.3", false),
1883 ("2.*.*", "3.1.3", false),
1884 ("1.2.* || 2.*", "3.1.3", false),
1885 ("2", "3.1.2", false),
1886 ("2.3", "2.4.1", false),
1887 ("~>3.2.1", "3.3.2", false),
1888 ("~>1", "2.2.3", false),
1889 ("~1.0", "1.1.0", false),
1890 ("<1", "1.0.0", false),
1891 ("<1", "1.0.0beta", false),
1892 ("< 1", "1.0.0beta", false),
1893 ("=0.7.x", "0.8.2", false),
1894 ("<0.7.x", "0.7.2", false),
1895 ("0.7.x", "0.7.2-beta", false),
1896 ];
1897
1898 const VERSION_NOT_GT_RANGE: &[(&str, &str, bool)] = &[
1899 ("~0.6.1-1", "0.6.1-1", false),
1900 ("1.0.0 - 2.0.0", "1.2.3", false),
1901 ("1.0.0 - 2.0.0", "0.9.9", false),
1902 ("1.0.0", "1.0.0", false),
1903 (">=*", "0.2.4", false),
1904 ("", "1.0.0", false),
1905 ("*", "1.2.3", false),
1906 ("*", "v1.2.3-foo", false),
1907 (">=1.0.0", "1.0.0", false),
1908 (">=1.0.0", "1.0.1", false),
1909 (">=1.0.0", "1.1.0", false),
1910 (">1.0.0", "1.0.1", false),
1911 (">1.0.0", "1.1.0", false),
1912 ("<=2.0.0", "2.0.0", false),
1913 ("<=2.0.0", "1.9999.9999", false),
1914 ("<=2.0.0", "0.2.9", false),
1915 ("<2.0.0", "1.9999.9999", false),
1916 ("<2.0.0", "0.2.9", false),
1917 (">= 1.0.0", "1.0.0", false),
1918 (">= 1.0.0", "1.0.1", false),
1919 (">= 1.0.0", "1.1.0", false),
1920 ("> 1.0.0", "1.0.1", false),
1921 ("> 1.0.0", "1.1.0", false),
1922 ("<= 2.0.0", "2.0.0", false),
1923 ("<= 2.0.0", "1.9999.9999", false),
1924 ("<= 2.0.0", "0.2.9", false),
1925 ("< 2.0.0", "1.9999.9999", false),
1926 ("<\t2.0.0", "0.2.9", false),
1927 (">=0.1.97", "v0.1.97", false),
1928 (">=0.1.97", "0.1.97", false),
1929 ("0.1.20 || 1.2.4", "1.2.4", false),
1930 ("0.1.20 || >1.2.4", "1.2.4", false),
1931 ("0.1.20 || 1.2.4", "1.2.3", false),
1932 ("0.1.20 || 1.2.4", "0.1.20", false),
1933 (">=0.2.3 || <0.0.1", "0.0.0", false),
1934 (">=0.2.3 || <0.0.1", "0.2.3", false),
1935 (">=0.2.3 || <0.0.1", "0.2.4", false),
1936 ("||", "1.3.4", false),
1937 ("2.x.x", "2.1.3", false),
1938 ("1.2.x", "1.2.3", false),
1939 ("1.2.x || 2.x", "2.1.3", false),
1940 ("1.2.x || 2.x", "1.2.3", false),
1941 ("x", "1.2.3", false),
1942 ("2.*.*", "2.1.3", false),
1943 ("1.2.*", "1.2.3", false),
1944 ("1.2.* || 2.*", "2.1.3", false),
1945 ("1.2.* || 2.*", "1.2.3", false),
1946 ("2", "2.1.2", false),
1947 ("2.3", "2.3.1", false),
1948 ("~2.4", "2.4.0", false),
1949 ("~2.4", "2.4.5", false),
1950 ("~>3.2.1", "3.2.2", false),
1951 ("~1", "1.2.3", false),
1952 ("~>1", "1.2.3", false),
1953 ("~> 1", "1.2.3", false),
1954 ("~1.0", "1.0.2", false),
1955 ("~ 1.0", "1.0.2", false),
1956 (">=1", "1.0.0", false),
1957 (">= 1", "1.0.0", false),
1958 ("<1.2", "1.1.1", false),
1959 ("< 1.2", "1.1.1", false),
1960 ("1", "1.0.0beta", false),
1961 ("~v0.5.4-pre", "0.5.5", false),
1962 ("~v0.5.4-pre", "0.5.4", false),
1963 ("=0.7.x", "0.7.2", false),
1964 (">=0.7.x", "0.7.2", false),
1965 ("=0.7.x", "0.7.0-asdf", false),
1966 (">=0.7.x", "0.7.0-asdf", false),
1967 ("<=0.7.x", "0.6.2", false),
1968 (">0.2.3 >0.2.4 <=0.2.5", "0.2.5", false),
1969 (">=0.2.3 <=0.2.4", "0.2.4", false),
1970 ("1.0.0 - 2.0.0", "2.0.0", false),
1971 ("^1", "0.0.0-0", false),
1972 ("^3.0.0", "2.0.0", false),
1973 ("^1.0.0 || ~2.0.1", "2.0.0", false),
1974 ("^0.1.0 || ~3.0.1 || 5.0.0", "3.2.0", false),
1975 ("^0.1.0 || ~3.0.1 || 5.0.0", "1.0.0beta", false),
1976 ("^0.1.0 || ~3.0.1 || 5.0.0", "5.0.0-0", false),
1977 ("^0.1.0 || ~3.0.1 || >4 <=5.0.0", "3.5.0", false),
1978 ("0.7.x", "0.7.2-beta", true),
1979 ];
1980
1981 const VERSION_LT_RANGE: &[(&str, &str, bool)] = &[
1982 ("~1.2.2", "1.2.1", false),
1983 ("~0.6.1-1", "0.6.1-0", false),
1984 ("1.0.0 - 2.0.0", "0.0.1", false),
1985 ("1.0.0-beta.2", "1.0.0-beta.1", false),
1986 ("1.0.0", "0.0.0", false),
1987 (">=2.0.0", "1.1.1", false),
1988 (">=2.0.0", "1.2.9", false),
1989 (">2.0.0", "2.0.0", false),
1990 ("0.1.20 || 1.2.4", "0.1.5", false),
1991 ("2.x.x", "1.0.0", false),
1992 ("1.2.x", "1.1.0", false),
1993 ("1.2.x || 2.x", "1.0.0", false),
1994 ("2.*.*", "1.0.1", false),
1995 ("1.2.*", "1.1.3", false),
1996 ("1.2.* || 2.*", "1.1.9999", false),
1997 ("2", "1.0.0", false),
1998 ("2.3", "2.2.2", false),
1999 ("~2.4", "2.3.0", false),
2000 ("~2.4", "2.3.5", false),
2001 ("~>3.2.1", "3.2.0", false),
2002 ("~1", "0.2.3", false),
2003 ("~>1", "0.2.4", false),
2004 ("~> 1", "0.2.3", false),
2005 ("~1.0", "0.1.2", false),
2006 ("~ 1.0", "0.1.0", false),
2007 (">1.2", "1.2.0", false),
2008 ("> 1.2", "1.2.1", false),
2009 ("1", "0.0.0beta", false),
2010 ("~v0.5.4-pre", "0.5.4-alpha", false),
2011 ("=0.7.x", "0.6.0", false),
2012 ("=0.7.x", "0.6.0-asdf", false),
2013 (">=0.7.x", "0.6.0", false),
2014 ("1.0.0 - 2.0.0", "0.2.3", false),
2015 ("1.0.0", "0.0.1", false),
2016 (">=2.0.0", "1.0.0", false),
2017 (">=2.0.0", "1.9999.9999", false),
2018 (">2.0.0", "1.2.9", false),
2019 ("2.x.x", "1.1.3", false),
2020 ("1.2.x", "1.1.3", false),
2021 ("1.2.x || 2.x", "1.1.3", false),
2022 ("2.*.*", "1.1.3", false),
2023 ("1.2.* || 2.*", "1.1.3", false),
2024 ("2", "1.9999.9999", false),
2025 ("2.3", "2.2.1", false),
2026 ("~>3.2.1", "2.3.2", false),
2027 ("~>1", "0.2.3", false),
2028 ("~1.0", "0.0.0", false),
2029 (">1", "1.0.0", false),
2030 ("2", "1.0.0beta", false),
2031 (">1", "1.0.0beta", false),
2032 ("> 1", "1.0.0beta", false),
2033 ("=0.7.x", "0.6.2", false),
2034 ("=0.7.x", "0.7.0-asdf", false),
2035 ("^1", "1.0.0-0", false),
2036 (">=0.7.x", "0.7.0-asdf", false),
2037 ("1", "1.0.0beta", false),
2038 (">=0.7.x", "0.6.2", false),
2039 (">1.2.3", "1.3.0-alpha", false),
2040 ];
2041
2042 const VERSION_NOT_LT_RANGE: &[(&str, &str, bool)] = &[
2043 ("~ 1.0", "1.1.0", false),
2044 ("~0.6.1-1", "0.6.1-1", false),
2045 ("1.0.0 - 2.0.0", "1.2.3", false),
2046 ("1.0.0 - 2.0.0", "2.9.9", false),
2047 ("1.0.0", "1.0.0", false),
2048 (">=*", "0.2.4", false),
2049 ("", "1.0.0", false),
2050 ("*", "1.2.3", false),
2051 (">=1.0.0", "1.0.0", false),
2052 (">=1.0.0", "1.0.1", false),
2053 (">=1.0.0", "1.1.0", false),
2054 (">1.0.0", "1.0.1", false),
2055 (">1.0.0", "1.1.0", false),
2056 ("<=2.0.0", "2.0.0", false),
2057 ("<=2.0.0", "1.9999.9999", false),
2058 ("<=2.0.0", "0.2.9", false),
2059 ("<2.0.0", "1.9999.9999", false),
2060 ("<2.0.0", "0.2.9", false),
2061 (">= 1.0.0", "1.0.0", false),
2062 (">= 1.0.0", "1.0.1", false),
2063 (">= 1.0.0", "1.1.0", false),
2064 ("> 1.0.0", "1.0.1", false),
2065 ("> 1.0.0", "1.1.0", false),
2066 ("<= 2.0.0", "2.0.0", false),
2067 ("<= 2.0.0", "1.9999.9999", false),
2068 ("<= 2.0.0", "0.2.9", false),
2069 ("< 2.0.0", "1.9999.9999", false),
2070 ("<\t2.0.0", "0.2.9", false),
2071 (">=0.1.97", "v0.1.97", false),
2072 (">=0.1.97", "0.1.97", false),
2073 ("0.1.20 || 1.2.4", "1.2.4", false),
2074 ("0.1.20 || >1.2.4", "1.2.4", false),
2075 ("0.1.20 || 1.2.4", "1.2.3", false),
2076 ("0.1.20 || 1.2.4", "0.1.20", false),
2077 (">=0.2.3 || <0.0.1", "0.0.0", false),
2078 (">=0.2.3 || <0.0.1", "0.2.3", false),
2079 (">=0.2.3 || <0.0.1", "0.2.4", false),
2080 ("||", "1.3.4", false),
2081 ("2.x.x", "2.1.3", false),
2082 ("1.2.x", "1.2.3", false),
2083 ("1.2.x || 2.x", "2.1.3", false),
2084 ("1.2.x || 2.x", "1.2.3", false),
2085 ("x", "1.2.3", false),
2086 ("2.*.*", "2.1.3", false),
2087 ("1.2.*", "1.2.3", false),
2088 ("1.2.* || 2.*", "2.1.3", false),
2089 ("1.2.* || 2.*", "1.2.3", false),
2090 ("2", "2.1.2", false),
2091 ("2.3", "2.3.1", false),
2092 ("~2.4", "2.4.0", false),
2093 ("~2.4", "2.4.5", false),
2094 ("~>3.2.1", "3.2.2", false),
2095 ("~1", "1.2.3", false),
2096 ("~>1", "1.2.3", false),
2097 ("~> 1", "1.2.3", false),
2098 ("~1.0", "1.0.2", false),
2099 ("~ 1.0", "1.0.2", false),
2100 (">=1", "1.0.0", false),
2101 (">= 1", "1.0.0", false),
2102 ("<1.2", "1.1.1", false),
2103 ("< 1.2", "1.1.1", false),
2104 ("~v0.5.4-pre", "0.5.5", false),
2105 ("~v0.5.4-pre", "0.5.4", false),
2106 ("=0.7.x", "0.7.2", false),
2107 (">=0.7.x", "0.7.2", false),
2108 ("<=0.7.x", "0.6.2", false),
2109 (">0.2.3 >0.2.4 <=0.2.5", "0.2.5", false),
2110 (">=0.2.3 <=0.2.4", "0.2.4", false),
2111 ("1.0.0 - 2.0.0", "2.0.0", false),
2112 ("^3.0.0", "4.0.0", false),
2113 ("^1.0.0 || ~2.0.1", "2.0.0", false),
2114 ("^0.1.0 || ~3.0.1 || 5.0.0", "3.2.0", false),
2115 ("^0.1.0 || ~3.0.1 || 5.0.0", "1.0.0beta", false),
2116 ("^0.1.0 || ~3.0.1 || 5.0.0", "5.0.0-0", false),
2117 ("^0.1.0 || ~3.0.1 || >4 <=5.0.0", "3.5.0", false),
2118 ("^1.0.0alpha", "1.0.0beta", false),
2119 ("~1.0.0alpha", "1.0.0beta", false),
2120 ("^1.0.0-alpha", "1.0.0beta", false),
2121 ("~1.0.0-alpha", "1.0.0beta", false),
2122 ("^1.0.0-alpha", "1.0.0-beta", false),
2123 ("~1.0.0-alpha", "1.0.0-beta", false),
2124 ("=0.1.0", "1.0.0", false),
2125 (">1.2.3", "1.3.0-alpha", true),
2126 ];
2127
2128 fn assert_outside(
2129 cases: &[(&str, &str, bool)],
2130 direction: OutsideDirection,
2131 include_prerelease: bool,
2132 expected: bool,
2133 ) {
2134 for (range, version, explicit_include) in cases {
2135 let include_prerelease = include_prerelease || *explicit_include;
2136 let range = Range::parse(range).unwrap();
2137 let version = Version::parse(version).unwrap();
2138
2139 let result = range
2140 .outside(&version, direction, include_prerelease)
2141 .expect("outside should always have comparator bounds");
2142 let message = format!(
2143 "{}outside({}, {}, {}, include_prerelease={})",
2144 if expected { "" } else { "!" },
2145 version,
2146 range,
2147 direction,
2148 include_prerelease
2149 );
2150
2151 if expected {
2152 assert!(result, "{}", message);
2153 } else {
2154 assert!(!result, "{}", message);
2155 }
2156 }
2157 }
2158
2159 #[test]
2160 fn greater_than_range() {
2161 assert_outside(VERSION_GT_RANGE, OutsideDirection::Higher, false, true);
2162 }
2163
2164 #[test]
2165 fn less_than_range() {
2166 assert_outside(VERSION_LT_RANGE, OutsideDirection::Lower, false, true);
2167 }
2168
2169 #[test]
2170 fn not_greater_than_range() {
2171 assert_outside(VERSION_NOT_GT_RANGE, OutsideDirection::Higher, false, false);
2172 }
2173
2174 #[test]
2175 fn not_less_than_range() {
2176 assert_outside(VERSION_NOT_LT_RANGE, OutsideDirection::Lower, false, false);
2177 }
2178
2179 #[test]
2180 fn outside_with_bad_direction_returns_error() {
2181 let range = Range::parse(">1.5.0").unwrap();
2182 let version = Version::parse("1.2.3").unwrap();
2183
2184 let result = OutsideDirection::try_from('x')
2185 .and_then(|direction| range.outside(&version, direction, false));
2186
2187 assert!(matches!(
2188 result,
2189 Err(RangeError::InvalidOutsideDirection('x'))
2190 ));
2191 }
2192}
2193
2194#[cfg(test)]
2195mod satisfies_ranges_tests {
2196 use super::*;
2197
2198 macro_rules! refute {
2199 ($e:expr) => {
2200 assert!(!$e)
2201 };
2202 ($e:expr, $msg:expr) => {
2203 assert!(!$e, $msg)
2204 };
2205 }
2206
2207 #[test]
2208 fn greater_than_equals() {
2209 let parsed = Range::parse(">=1.2.3").expect("unable to parse");
2210
2211 refute!(parsed.satisfies(&(0, 2, 3).into()), "major too low");
2212 refute!(parsed.satisfies(&(1, 1, 3).into()), "minor too low");
2213 refute!(parsed.satisfies(&(1, 2, 2).into()), "patch too low");
2214 assert!(parsed.satisfies(&(1, 2, 3).into()), "exact");
2215 assert!(parsed.satisfies(&(2, 2, 3).into()), "above");
2216 }
2217
2218 #[test]
2219 fn greater_than() {
2220 let parsed = Range::parse(">1.2.3").expect("unable to parse");
2221
2222 refute!(parsed.satisfies(&(0, 2, 3).into()), "major too low");
2223 refute!(parsed.satisfies(&(1, 1, 3).into()), "minor too low");
2224 refute!(parsed.satisfies(&(1, 2, 2).into()), "patch too low");
2225 refute!(parsed.satisfies(&(1, 2, 3).into()), "exact");
2226 assert!(parsed.satisfies(&(1, 2, 4).into()), "above");
2227 }
2228
2229 #[test]
2230 fn exact() {
2231 let parsed = Range::parse("=1.2.3").expect("unable to parse");
2232
2233 refute!(parsed.satisfies(&(1, 2, 2).into()), "patch too low");
2234 assert!(parsed.satisfies(&(1, 2, 3).into()), "exact");
2235 refute!(parsed.satisfies(&(1, 2, 4).into()), "above");
2236 }
2237
2238 #[test]
2239 fn less_than() {
2240 let parsed = Range::parse("<1.2.3").expect("unable to parse");
2241
2242 assert!(parsed.satisfies(&(0, 2, 3).into()), "major below");
2243 assert!(parsed.satisfies(&(1, 1, 3).into()), "minor below");
2244 assert!(parsed.satisfies(&(1, 2, 2).into()), "patch below");
2245 refute!(parsed.satisfies(&(1, 2, 3).into()), "exact");
2246 refute!(parsed.satisfies(&(1, 2, 4).into()), "above");
2247 }
2248
2249 #[test]
2250 fn less_than_equals() {
2251 let parsed = Range::parse("<=1.2.3").expect("unable to parse");
2252
2253 assert!(parsed.satisfies(&(0, 2, 3).into()), "major below");
2254 assert!(parsed.satisfies(&(1, 1, 3).into()), "minor below");
2255 assert!(parsed.satisfies(&(1, 2, 2).into()), "patch below");
2256 assert!(parsed.satisfies(&(1, 2, 3).into()), "exact");
2257 refute!(parsed.satisfies(&(1, 2, 4).into()), "above");
2258 }
2259
2260 #[test]
2261 fn less_than_equals_major() {
2262 let parsed = Range::parse("<=1").expect("unable to parse");
2263
2264 assert!(parsed.satisfies(&(0, 2, 3).into()), "major below");
2265 assert!(parsed.satisfies(&(1, 1, 3).into()), "minor below");
2266 assert!(parsed.satisfies(&(1, 2, 2).into()), "minor below");
2267 assert!(parsed.satisfies(&(1, 2, 3).into()), "minor below");
2268 assert!(parsed.satisfies(&(1, 2, 4).into()), "minor below");
2269 refute!(parsed.satisfies(&(2, 0, 0).into()), "above");
2270 }
2271
2272 #[test]
2273 fn less_than_equals_minor() {
2274 let parsed = Range::parse("<=1.2").expect("unable to parse");
2275
2276 assert!(parsed.satisfies(&(0, 2, 3).into()), "major below");
2277 assert!(parsed.satisfies(&(1, 1, 3).into()), "minor below");
2278 assert!(parsed.satisfies(&(1, 2, 1).into()), "patch below");
2279 assert!(parsed.satisfies(&(1, 2, 5).into()), "patch below");
2280 refute!(parsed.satisfies(&(1, 3, 0).into()), "above");
2281 }
2282
2283 #[test]
2284 fn only_major() {
2285 let parsed = Range::parse("1").expect("unable to parse");
2286
2287 refute!(parsed.satisfies(&(0, 2, 3).into()), "major below");
2288 assert!(parsed.satisfies(&(1, 0, 0).into()), "exact bottom of range");
2289 assert!(parsed.satisfies(&(1, 2, 2).into()), "middle");
2290 refute!(parsed.satisfies(&(2, 0, 0).into()), "exact top of range");
2291 refute!(parsed.satisfies(&(2, 7, 3).into()), "above");
2292 }
2293
2294 #[test]
2295 fn pre_release_version() {
2296 let range = Range::parse("^2").unwrap();
2297
2298 refute!(
2299 range.satisfies(&Version::parse("2.0.0-alpha.0").unwrap()),
2300 "below"
2301 );
2302 refute!(
2303 range.satisfies(&Version::parse("2.1.0-alpha.0").unwrap()),
2304 "above but pre-release"
2305 );
2306 }
2307
2308 #[test]
2309 fn pre_release_range() {
2310 let range = Range::parse("^1.2.3-rc.4").unwrap();
2311
2312 refute!(range.satisfies(&Version::parse("1.2.2").unwrap()), "below");
2313 assert!(
2314 range.satisfies(&Version::parse("1.2.3").unwrap()),
2315 "equal non-prerelease"
2316 );
2317 assert!(range.satisfies(&Version::parse("1.2.4").unwrap()), "above");
2318 }
2319
2320 #[test]
2321 fn pre_release_version_and_range() {
2322 let range = Range::parse("^1.2.3-rc.4").unwrap();
2323
2324 refute!(
2325 range.satisfies(&Version::parse("1.2.3-rc.3").unwrap()),
2326 "below"
2327 );
2328 assert!(
2329 range.satisfies(&Version::parse("1.2.3-rc.4").unwrap()),
2330 "equal"
2331 );
2332 assert!(
2333 range.satisfies(&Version::parse("1.2.3-rc.5").unwrap()),
2334 "above"
2335 );
2336 refute!(
2337 range.satisfies(&Version::parse("1.2.4-rc.6").unwrap()),
2338 "above patch but pre-release"
2339 );
2340 }
2341
2342 #[test]
2343 fn npm_compatibility_cases() {
2344 let cases = [
2345 ("3.4.5", ">=3.3.0-beta.1 <3.4.0-beta.3", false),
2346 ("1.0.0", "1.0.x", true),
2347 ("1.2.3", "1.x.x", true),
2348 ("1.2.3", "x.x.x", true),
2349 ("1.0.1", "1.0.0 - 1.0.x", true),
2350 ("2.0.0", "1.0.0 - 1.x", false),
2351 ("1.2.3", "^1 || ^2", true),
2352 ("2.0.0-beta.1", "^1 || ^2", false),
2353 ];
2354
2355 for (version, range, expected) in cases {
2356 let version = Version::parse(version).unwrap();
2357 let range = Range::parse(range).unwrap();
2358
2359 assert_eq!(
2360 range.satisfies(&version),
2361 expected,
2362 "expected satisfies({}, {}) to be {}",
2363 version,
2364 range,
2365 expected
2366 );
2367 }
2368 }
2369}
2370
2371#[cfg(test)]
2373mod tests {
2374 use super::*;
2375
2376 use pretty_assertions::assert_eq;
2377
2378 macro_rules! range_parse_tests {
2379 ($($name:ident => $vals:expr),+ ,$(,)?) => {
2380 $(
2381 #[test]
2382 fn $name() {
2383 let [input, expected] = $vals;
2384
2385 let parsed = Range::parse(input).expect("unable to parse");
2386
2387 assert_eq!(expected, parsed.to_string());
2388 }
2389 )+
2390 }
2391
2392 }
2393
2394 range_parse_tests![
2395 exact => ["1.0.0", "1.0.0"],
2397 major_minor_patch_range => ["1.0.0 - 2.0.0", ">=1.0.0 <=2.0.0"],
2398 only_major_versions => ["1 - 2", ">=1.0.0 <3.0.0-0"],
2399 only_major_and_minor => ["1.0 - 2.0", ">=1.0.0 <2.1.0-0"],
2400 mixed_major_minor => ["1.2 - 3.4.5", ">=1.2.0 <=3.4.5"],
2401 mixed_major_minor_2 => ["1.2.3 - 3.4", ">=1.2.3 <3.5.0-0"],
2402 minor_minor_range => ["1.2 - 3.4", ">=1.2.0 <3.5.0-0"],
2403 single_sided_only_major => ["1", ">=1.0.0 <2.0.0-0"],
2404 single_sided_lower_equals_bound => [">=1.0.0", ">=1.0.0"],
2405 single_sided_lower_equals_bound_2 => [">=0.1.97", ">=0.1.97"],
2406 single_sided_lower_bound => [">1.0.0", ">1.0.0"],
2407 single_sided_upper_equals_bound => ["<=2.0.0", "<=2.0.0"],
2408 single_sided_upper_equals_bound_with_minor => ["<=2.0", "<=2.0.900719925474099"],
2409 single_sided_upper_bound => ["<2.0.0", "<2.0.0"],
2410 major_and_minor => ["2.3", ">=2.3.0 <2.4.0-0"],
2411 major_dot_x => ["2.x", ">=2.0.0 <3.0.0-0"],
2412 x_and_asterisk_version => ["2.x.x", ">=2.0.0 <3.0.0-0"],
2413 patch_x => ["1.2.x", ">=1.2.0 <1.3.0-0"],
2414 minor_asterisk_patch_asterisk => ["2.*.*", ">=2.0.0 <3.0.0-0"],
2415 patch_asterisk => ["1.2.*", ">=1.2.0 <1.3.0-0"],
2416 caret_zero => ["^0", "<1.0.0-0"],
2417 caret_zero_minor => ["^0.1", ">=0.1.0 <0.2.0-0"],
2418 caret_one => ["^1.0", ">=1.0.0 <2.0.0-0"],
2419 caret_minor => ["^1.2", ">=1.2.0 <2.0.0-0"],
2420 caret_patch => ["^0.0.1", ">=0.0.1 <0.0.2-0"],
2421 caret_with_patch => ["^0.1.2", ">=0.1.2 <0.2.0-0"],
2422 caret_with_patch_2 => ["^1.2.3", ">=1.2.3 <2.0.0-0"],
2423 tilde_one => ["~1", ">=1.0.0 <2.0.0-0"],
2424 tilde_minor => ["~1.0", ">=1.0.0 <1.1.0-0"],
2425 tilde_minor_2 => ["~2.4", ">=2.4.0 <2.5.0-0"],
2426 tilde_with_greater_than_patch => ["~>3.2.1", ">=3.2.1 <3.3.0-0"],
2427 tilde_major_minor_zero => ["~1.1.0", ">=1.1.0 <1.2.0-0"],
2428 grater_than_equals_one => [">=1", ">=1.0.0"],
2429 greater_than_one => [">1", ">=2.0.0"],
2430 less_than_one_dot_two => ["<1.2", "<1.2.0-0"],
2431 greater_than_one_dot_two => [">1.2", ">=1.3.0"],
2432 greater_than_with_prerelease => [">1.1.0-beta-10", ">1.1.0-beta-10"],
2433 either_one_version_or_the_other => ["0.1.20 || 1.2.4", "0.1.20||1.2.4"],
2434 either_one_version_range_or_another => [">=0.2.3 || <0.0.1", ">=0.2.3||<0.0.1"],
2435 either_x_version_works => ["1.2.x || 2.x", ">=1.2.0 <1.3.0-0||>=2.0.0 <3.0.0-0"],
2436 either_asterisk_version_works => ["1.2.* || 2.*", ">=1.2.0 <1.3.0-0||>=2.0.0 <3.0.0-0"],
2437 one_two_three_or_greater_than_four => ["1.2.3 || >4", "1.2.3||>=5.0.0"],
2438 any_version_asterisk => ["*", ">=0.0.0"],
2439 any_version_x => ["x", ">=0.0.0"],
2440 any_version_upper_x => ["X", ">=0.0.0"],
2441 greater_than_equals_x => [">=x", ">=0.0.0"],
2442 whitespace_1 => [">= 1.0.0", ">=1.0.0"],
2443 whitespace_2 => [">= 1.0.0", ">=1.0.0"],
2444 whitespace_3 => [">= 1.0.0", ">=1.0.0"],
2445 whitespace_4 => ["> 1.0.0", ">1.0.0"],
2446 whitespace_5 => ["> 1.0.0", ">1.0.0"],
2447 whitespace_6 => ["<= 2.0.0", "<=2.0.0"],
2448 whitespace_7 => ["<= 2.0.0", "<=2.0.0"],
2449 whitespace_8 => ["<= 2.0.0", "<=2.0.0"],
2450 whitespace_9 => ["< 2.0.0", "<2.0.0"],
2451 whitespace_10 => ["<\t2.0.0", "<2.0.0"],
2452 whitespace_11 => ["^ 1", ">=1.0.0 <2.0.0-0"],
2453 whitespace_12 => ["~> 1", ">=1.0.0 <2.0.0-0"],
2454 whitespace_13 => ["~ 1.0", ">=1.0.0 <1.1.0-0"],
2455 beta => ["^0.0.1-beta", ">=0.0.1-beta <0.0.2-0"],
2456 beta_tilde => ["~1.2.3-beta", ">=1.2.3-beta <1.3.0-0"],
2457 beta_4 => ["^1.2.3-beta.4", ">=1.2.3-beta.4 <2.0.0-0"],
2458 pre_release_on_both => ["1.0.0-alpha - 2.0.0-beta", ">=1.0.0-alpha <=2.0.0-beta"],
2459 single_sided_lower_bound_with_pre_release => [">1.0.0-alpha", ">1.0.0-alpha"],
2460 space_separated1 => [">=1.2.3 <4.5.6", ">=1.2.3 <4.5.6"],
2461 garbage1 => ["1.2.3 foo", "1.2.3"],
2462 garbage2 => ["foo 1.2.3", "1.2.3"],
2463 garbage3 => ["~1.y 1.2.3", "1.2.3"],
2464 garbage4 => ["1.2.3 ~1.y", "1.2.3"],
2465 loose1 => [">01.02.03", ">1.2.3"],
2466 loose2 => ["~1.2.3beta", ">=1.2.3-beta <1.3.0-0"],
2467 caret_weird => ["^ 1.2 ^ 1", ">=1.2.0 <2.0.0-0"],
2468 loose_eq1 => ["=0.7", ">=0.7.0 <0.8.0-0"],
2469 loose_eq2 => ["=1", ">=1.0.0 <2.0.0-0"],
2470 consistent => ["^1.0.1", ">=1.0.1 <2.0.0-0"],
2471 consistent2 => [">=1.0.1 <2.0.0-0", ">=1.0.1 <2.0.0-0"],
2472 ];
2473
2474 #[test]
2475 fn rejects_single_token_garbage_and_protocols() {
2476 for input in [
2477 "foo",
2478 "workspace:*",
2479 "npm:react-dom@19.3.0-canary-b1786c31-20260618",
2480 ] {
2481 assert_eq!(
2482 Range::parse(input).unwrap_err().kind(),
2483 &SemverErrorKind::NoValidRanges
2484 );
2485 }
2486
2487 assert_eq!(Range::parse("foo 1.2.3").unwrap().to_string(), "1.2.3");
2488 }
2489
2490 }
2497
2498#[cfg(test)]
2499mod ranges {
2500 use super::*;
2501
2502 #[test]
2503 fn one() {
2504 let r = BoundSet::new(
2505 Bound::Lower(Predicate::Including((1, 2, 0).into())),
2506 Bound::Upper(Predicate::Excluding((3, 3, 4).into())),
2507 )
2508 .unwrap();
2509
2510 assert_eq!(r.to_string(), ">=1.2.0 <3.3.4")
2511 }
2512}
2513
2514#[cfg(test)]
2515mod max_satisfying {
2516 use super::*;
2517
2518 fn assert_max_satisfying(versions: Vec<&str>, range: &str, expected: &str) {
2519 let versions: Vec<_> = versions
2520 .into_iter()
2521 .map(|s| Version::parse(s).unwrap())
2522 .collect();
2523 let range = Range::parse(range).unwrap();
2524 let result = range.max_satisfying(&versions);
2525
2526 assert_eq!(
2527 result,
2528 Some(&Version::parse(expected).unwrap()),
2529 "expected: {}, got: {:?}",
2530 expected,
2531 result
2532 );
2533 }
2534
2535 #[test]
2536 fn test_max_satisfying() {
2537 let cases = vec![
2538 (vec!["1.2.3", "1.2.4"], "1.2", "1.2.4"),
2539 (vec!["1.2.4", "1.2.3"], "1.2", "1.2.4"),
2540 (vec!["1.2.3", "1.2.4", "1.2.5", "1.2.6"], "~1.2.3", "1.2.6"),
2541 (
2542 vec!["1.1.0", "1.2.0", "1.2.1", "1.3.0", "2.0.0", "2.1.0"],
2543 "~2.0.0",
2544 "2.0.0",
2545 ),
2546 ];
2547
2548 for case in cases {
2549 assert_max_satisfying(case.0, case.1, case.2);
2550 }
2551 }
2552
2553 #[test]
2554 fn test_max_satisfying_empty() {
2555 let range = Range::parse("~1.2.3").unwrap();
2556 let versions = vec![];
2557 let result = range.max_satisfying(&versions);
2558
2559 assert_eq!(result, None);
2560 }
2561
2562 #[test]
2563 fn test_max_satisfying_none() {
2564 let range = Range::parse(">=1.0.0 <2.0.0").unwrap();
2565 let versions: Vec<_> = vec!["2.0.0", "0.1.0"]
2566 .iter()
2567 .map(|s| Version::parse(s).unwrap())
2568 .collect();
2569 let result = range.max_satisfying(&versions);
2570
2571 assert_eq!(result, None);
2572 }
2573}
2574
2575#[cfg(test)]
2576mod min_satisfying {
2577 use super::*;
2578
2579 fn assert_min_satisfying(versions: Vec<&str>, range: &str, expected: &str) {
2580 let versions: Vec<_> = versions
2581 .into_iter()
2582 .map(|s| Version::parse(s).unwrap())
2583 .collect();
2584 let range = Range::parse(range).unwrap();
2585 let result = range.min_satisfying(&versions);
2586
2587 assert_eq!(
2588 result,
2589 Some(&Version::parse(expected).unwrap()),
2590 "expected: {}, got: {:?}",
2591 expected,
2592 result
2593 );
2594 }
2595
2596 #[test]
2597 fn test_min_satisfying() {
2598 let cases = vec![
2599 (vec!["1.2.3", "1.2.4"], "1.2", "1.2.3"),
2600 (vec!["1.2.4", "1.2.3"], "1.2", "1.2.3"),
2601 (vec!["1.2.3", "1.2.4", "1.2.5", "1.2.6"], "~1.2.3", "1.2.3"),
2602 (
2603 vec!["1.1.0", "1.2.0", "1.2.1", "1.3.0", "2.0.0", "2.1.0"],
2604 "~2.0.0",
2605 "2.0.0",
2606 ),
2607 ];
2608
2609 for case in cases {
2610 assert_min_satisfying(case.0, case.1, case.2);
2611 }
2612 }
2613
2614 #[test]
2615 fn test_min_satisfying_empty() {
2616 let range = Range::parse("~1.2.3").unwrap();
2617 let versions = vec![];
2618 let result = range.min_satisfying(&versions);
2619
2620 assert_eq!(result, None);
2621 }
2622
2623 #[test]
2624 fn test_min_satisfying_none() {
2625 let range = Range::parse(">=1.0.0 <2.0.0").unwrap();
2626 let versions: Vec<_> = vec!["2.0.0", "0.1.0"]
2627 .iter()
2628 .map(|s| Version::parse(s).unwrap())
2629 .collect();
2630 let result = range.min_satisfying(&versions);
2631
2632 assert_eq!(result, None);
2633 }
2634}
2635
2636#[cfg(test)]
2637mod min_version {
2638 use super::*;
2639
2640 #[test]
2641 fn min_version_test() {
2642 let tests = vec![
2644 ("*", Some("0.0.0")),
2646 ("* || >=2", Some("0.0.0")),
2647 (">=2 || *", Some("0.0.0")),
2648 (">2 || *", Some("0.0.0")),
2649 ("1.0.0", Some("1.0.0")),
2651 ("1.0", Some("1.0.0")),
2652 ("1.0.x", Some("1.0.0")),
2653 ("1.0.*", Some("1.0.0")),
2654 ("1", Some("1.0.0")),
2655 ("1.x.x", Some("1.0.0")),
2656 ("1.x.x", Some("1.0.0")),
2657 ("1.*.x", Some("1.0.0")),
2658 ("1.x.*", Some("1.0.0")),
2659 ("1.x", Some("1.0.0")),
2660 ("1.*", Some("1.0.0")),
2661 ("=1.0.0", Some("1.0.0")),
2662 ("~1.1.1", Some("1.1.1")),
2664 ("~1.1.1-beta", Some("1.1.1-beta")),
2665 ("~1.1.1 || >=2", Some("1.1.1")),
2666 ("^1.1.1", Some("1.1.1")),
2668 ("^1.1.1-beta", Some("1.1.1-beta")),
2669 ("^1.1.1 || >=2", Some("1.1.1")),
2670 ("^2.16.2 ^2.16", Some("2.16.2")),
2671 ("1.1.1 - 1.8.0", Some("1.1.1")),
2673 ("1.1 - 1.8.0", Some("1.1.0")),
2674 ("<2", Some("0.0.0")),
2676 ("<0.0.0-beta", Some("0.0.0-0")),
2677 ("<0.0.1-beta", Some("0.0.0")),
2678 ("<2 || >4", Some("0.0.0")),
2679 (">4 || <2", Some("0.0.0")),
2680 ("<=2 || >=4", Some("0.0.0")),
2681 (">=4 || <=2", Some("0.0.0")),
2682 ("<0.0.0-beta >0.0.0-alpha", Some("0.0.0-alpha.0")),
2683 (">0.0.0-alpha <0.0.0-beta", Some("0.0.0-alpha.0")),
2684 (">=1.1.1 <2 || >=2.2.2 <3", Some("1.1.1")),
2686 (">=2.2.2 <3 || >=1.1.1 <2", Some("1.1.1")),
2687 (">1.0.0", Some("1.0.1")),
2689 (">1.0.0-0", Some("1.0.0-0.0")),
2690 (">1.0.0-beta", Some("1.0.0-beta.0")),
2691 (">2 || >1.0.0", Some("1.0.1")),
2692 (">2 || >1.0.0-0", Some("1.0.0-0.0")),
2693 (">2 || >1.0.0-beta", Some("1.0.0-beta.0")),
2694 ];
2695
2696 for (range, version) in tests {
2697 let parsed_range = Range::parse(range).unwrap();
2698 let parsed_version = version.map(|v| Version::parse(v).unwrap());
2699 assert_eq!(
2700 parsed_range.min_version(),
2701 parsed_version,
2702 "expected min_version of {:?} to be {:?}",
2703 range,
2704 version
2705 );
2706 }
2707 }
2708}
2709
2710#[cfg(feature = "serde")]
2711#[cfg(test)]
2712mod serde_tests {
2713 use super::*;
2714
2715 #[test]
2716 fn test_serialize() {
2717 let range = Range::parse("~1.2.3").unwrap();
2718 let serialized = serde_json::to_string(&range).unwrap();
2719 let deserialized: Range = serde_json::from_str(&serialized).unwrap();
2720
2721 assert_eq!(range, deserialized);
2722 }
2723}