nodejs_semver/
range.rs

1use std::cmp::{Ord, Ordering, PartialOrd};
2use std::fmt;
3
4use winnow::ascii::{space0, space1};
5use winnow::combinator::{
6    alt, delimited, eof, opt, peek, preceded, repeat_till, separated, terminated,
7};
8use winnow::error::ErrMode;
9use winnow::token::{any, literal};
10use winnow::{PResult, Parser};
11
12#[cfg(feature = "serde")]
13use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize};
14
15use crate::{
16    extras, number, Identifier, SemverError, SemverErrorKind, SemverParseError, Version,
17    MAX_SAFE_INTEGER,
18};
19
20#[derive(Clone, Debug, Eq, PartialEq, Hash)]
21struct BoundSet {
22    upper: Box<Bound>,
23    lower: Box<Bound>,
24}
25
26impl BoundSet {
27    fn new(lower: Bound, upper: Bound) -> Option<Self> {
28        use Bound::*;
29        use Predicate::*;
30
31        match (lower, upper) {
32            (Lower(Excluding(v1)), Upper(Including(v2)))
33            | (Lower(Including(v1)), Upper(Excluding(v2)))
34                if v1 == v2 =>
35            {
36                None
37            }
38            (Lower(Including(v1)), Upper(Including(v2))) if v1 == v2 => Some(Self {
39                lower: Box::new(Lower(Including(v1))),
40                upper: Box::new(Upper(Including(v2))),
41            }),
42            (lower, upper) if lower < upper => Some(Self {
43                lower: Box::new(lower),
44                upper: Box::new(upper),
45            }),
46            _ => None,
47        }
48    }
49
50    fn at_least(p: Predicate) -> Option<Self> {
51        BoundSet::new(Bound::Lower(p), Bound::upper())
52    }
53
54    fn at_most(p: Predicate) -> Option<Self> {
55        BoundSet::new(Bound::lower(), Bound::Upper(p))
56    }
57
58    fn exact(version: Version) -> Option<Self> {
59        BoundSet::new(
60            Bound::Lower(Predicate::Including(version.clone())),
61            Bound::Upper(Predicate::Including(version)),
62        )
63    }
64
65    fn satisfies(&self, version: &Version) -> bool {
66        use Bound::*;
67        use Predicate::*;
68
69        let lower_bound = match &self.lower.as_ref() {
70            Lower(Including(lower)) => lower <= version,
71            Lower(Excluding(lower)) => lower < version,
72            Lower(Unbounded) => true,
73            _ => unreachable!(
74                "There should not have been an upper bound: {:#?}",
75                self.lower
76            ),
77        };
78
79        let upper_bound = match &self.upper.as_ref() {
80            Upper(Including(upper)) => version <= upper,
81            Upper(Excluding(upper)) => version < upper,
82            Upper(Unbounded) => true,
83            _ => unreachable!(
84                "There should not have been an lower bound: {:#?}",
85                self.lower
86            ),
87        };
88
89        if !lower_bound || !upper_bound {
90            return false;
91        }
92
93        if version.is_prerelease() {
94            let lower_version = match &self.lower.as_ref() {
95                Lower(Including(v)) => Some(v),
96                Lower(Excluding(v)) => Some(v),
97                _ => None,
98            };
99            if let Some(lower_version) = lower_version {
100                if lower_version.is_prerelease()
101                    && version.major == lower_version.major
102                    && version.minor == lower_version.minor
103                    && version.patch == lower_version.patch
104                {
105                    return true;
106                }
107            }
108
109            let upper_version = match &self.upper.as_ref() {
110                Upper(Including(v)) => Some(v),
111                Upper(Excluding(v)) => Some(v),
112                _ => None,
113            };
114            if let Some(upper_version) = upper_version {
115                if upper_version.is_prerelease()
116                    && version.major == upper_version.major
117                    && version.minor == upper_version.minor
118                    && version.patch == upper_version.patch
119                {
120                    return true;
121                }
122            }
123
124            return false;
125        }
126
127        true
128    }
129
130    fn allows_all(&self, other: &BoundSet) -> bool {
131        self.lower <= other.lower && other.upper <= self.upper
132    }
133
134    fn allows_any(&self, other: &BoundSet) -> bool {
135        if other.upper < self.lower {
136            return false;
137        }
138
139        if self.upper < other.lower {
140            return false;
141        }
142
143        true
144    }
145
146    fn intersect(&self, other: &Self) -> Option<Self> {
147        let lower: &Bound = std::cmp::max(&self.lower, &other.lower);
148        let upper: &Bound = std::cmp::min(&self.upper, &other.upper);
149
150        BoundSet::new(lower.clone(), upper.clone())
151    }
152
153    fn difference(&self, other: &Self) -> Option<Vec<Self>> {
154        use Bound::*;
155
156        if let Some(overlap) = self.intersect(other) {
157            if &overlap == self {
158                return None;
159            }
160
161            if self.lower < overlap.lower && overlap.upper < self.upper {
162                return Some(vec![
163                    BoundSet::new(*self.lower.clone(), Upper(overlap.lower.predicate().flip()))
164                        .unwrap(),
165                    BoundSet::new(Lower(overlap.upper.predicate().flip()), *self.upper.clone())
166                        .unwrap(),
167                ]);
168            }
169
170            if self.lower < overlap.lower {
171                return BoundSet::new(*self.lower.clone(), Upper(overlap.lower.predicate().flip()))
172                    .map(|f| vec![f]);
173            }
174
175            BoundSet::new(Lower(overlap.upper.predicate().flip()), *self.upper.clone())
176                .map(|f| vec![f])
177        } else {
178            Some(vec![self.clone()])
179        }
180    }
181}
182
183impl fmt::Display for BoundSet {
184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185        use Bound::*;
186        use Predicate::*;
187        match (&self.lower.as_ref(), &self.upper.as_ref()) {
188            (Lower(Unbounded), Upper(Unbounded)) => write!(f, "*"),
189            (Lower(Unbounded), Upper(Including(v))) => write!(f, "<={}", v),
190            (Lower(Unbounded), Upper(Excluding(v))) => write!(f, "<{}", v),
191            (Lower(Including(v)), Upper(Unbounded)) => write!(f, ">={}", v),
192            (Lower(Excluding(v)), Upper(Unbounded)) => write!(f, ">{}", v),
193            (Lower(Including(v)), Upper(Including(v2))) if v == v2 => write!(f, "{}", v),
194            (Lower(Including(v)), Upper(Including(v2))) => write!(f, ">={} <={}", v, v2),
195            (Lower(Including(v)), Upper(Excluding(v2))) => write!(f, ">={} <{}", v, v2),
196            (Lower(Excluding(v)), Upper(Including(v2))) => write!(f, ">{} <={}", v, v2),
197            (Lower(Excluding(v)), Upper(Excluding(v2))) => write!(f, ">{} <{}", v, v2),
198            _ => unreachable!("does not make sense"),
199        }
200    }
201}
202
203#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
204enum Operation {
205    Exact,
206    GreaterThan,
207    GreaterThanEquals,
208    LessThan,
209    LessThanEquals,
210}
211
212#[derive(Debug, Clone, Eq, PartialEq, Hash)]
213enum Predicate {
214    Excluding(Version), // < and >
215    Including(Version), // <= and >=
216    Unbounded,          // *
217}
218
219impl Predicate {
220    fn flip(self) -> Self {
221        use Predicate::*;
222        match self {
223            Excluding(v) => Including(v),
224            Including(v) => Excluding(v),
225            Unbounded => Unbounded,
226        }
227    }
228}
229
230#[derive(Debug, Clone, Eq, PartialEq, Hash)]
231enum Bound {
232    Lower(Predicate),
233    Upper(Predicate),
234}
235
236impl Bound {
237    fn upper() -> Self {
238        Bound::Upper(Predicate::Unbounded)
239    }
240
241    fn lower() -> Self {
242        Bound::Lower(Predicate::Unbounded)
243    }
244
245    fn predicate(self) -> Predicate {
246        use Bound::*;
247
248        match self {
249            Lower(p) => p,
250            Upper(p) => p,
251        }
252    }
253}
254
255impl Ord for Bound {
256    fn cmp(&self, other: &Self) -> Ordering {
257        use Bound::*;
258        use Predicate::*;
259
260        match (self, other) {
261            (Lower(Unbounded), Lower(Unbounded)) | (Upper(Unbounded), Upper(Unbounded)) => {
262                Ordering::Equal
263            }
264            (Upper(Unbounded), _) | (_, Lower(Unbounded)) => Ordering::Greater,
265            (Lower(Unbounded), _) | (_, Upper(Unbounded)) => Ordering::Less,
266
267            (Upper(Including(v1)), Upper(Including(v2)))
268            | (Upper(Including(v1)), Lower(Including(v2)))
269            | (Upper(Excluding(v1)), Upper(Excluding(v2)))
270            | (Upper(Excluding(v1)), Lower(Excluding(v2)))
271            | (Lower(Including(v1)), Upper(Including(v2)))
272            | (Lower(Including(v1)), Lower(Including(v2)))
273            | (Lower(Excluding(v1)), Lower(Excluding(v2))) => v1.cmp(v2),
274
275            (Lower(Excluding(v1)), Upper(Excluding(v2)))
276            | (Lower(Including(v1)), Upper(Excluding(v2))) => {
277                if v2 <= v1 {
278                    Ordering::Greater
279                } else {
280                    Ordering::Less
281                }
282            }
283            (Upper(Including(v1)), Upper(Excluding(v2)))
284            | (Upper(Including(v1)), Lower(Excluding(v2)))
285            | (Lower(Excluding(v1)), Upper(Including(v2))) => {
286                if v2 < v1 {
287                    Ordering::Greater
288                } else {
289                    Ordering::Less
290                }
291            }
292            (Lower(Excluding(v1)), Lower(Including(v2))) => {
293                if v1 < v2 {
294                    Ordering::Less
295                } else {
296                    Ordering::Greater
297                }
298            }
299            (Lower(Including(v1)), Lower(Excluding(v2)))
300            | (Upper(Excluding(v1)), Lower(Including(v2)))
301            | (Upper(Excluding(v1)), Upper(Including(v2))) => {
302                if v1 <= v2 {
303                    Ordering::Less
304                } else {
305                    Ordering::Greater
306                }
307            }
308        }
309    }
310}
311
312impl PartialOrd for Bound {
313    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
314        Some(self.cmp(other))
315    }
316}
317
318/**
319Node-style semver range.
320
321These ranges map mostly 1:1 to semver's except for some internal representation
322details that allow some more interesting set-level operations.
323
324For details on supported syntax, see <https://github.com/npm/node-semver#advanced-range-syntax>
325*/
326#[derive(Clone, Debug, Eq, PartialEq, Hash)]
327pub struct Range(Vec<BoundSet>);
328
329impl fmt::Display for Operation {
330    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331        use Operation::*;
332        match self {
333            Exact => write!(f, ""),
334            GreaterThan => write!(f, ">"),
335            GreaterThanEquals => write!(f, ">="),
336            LessThan => write!(f, "<"),
337            LessThanEquals => write!(f, "<="),
338        }
339    }
340}
341
342#[cfg(feature = "serde")]
343impl Serialize for Range {
344    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
345        s.collect_str(self)
346    }
347}
348
349#[cfg(feature = "serde")]
350impl<'de> Deserialize<'de> for Range {
351    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
352        let s = String::deserialize(d)?;
353        s.parse().map_err(serde::de::Error::custom)
354    }
355}
356
357impl Range {
358    /**
359    Parse a range from a string.
360    */
361    pub fn parse<S: AsRef<str>>(input: S) -> Result<Self, SemverError> {
362        let mut input = input.as_ref();
363
364        match range_set.parse_next(&mut input) {
365            Ok(range) => Ok(range),
366            Err(err) => Err(match err {
367                ErrMode::Backtrack(e) | ErrMode::Cut(e) => SemverError {
368                    input: input.into(),
369                    span: (e.input.as_ptr() as usize - input.as_ptr() as usize, 0).into(),
370                    kind: if let Some(kind) = e.kind {
371                        kind
372                    } else if let Some(ctx) = e.context {
373                        SemverErrorKind::Context(ctx)
374                    } else {
375                        SemverErrorKind::Other
376                    },
377                },
378                ErrMode::Incomplete(_) => SemverError {
379                    input: input.into(),
380                    span: (input.len() - 1, 0).into(),
381                    kind: SemverErrorKind::IncompleteInput,
382                },
383            }),
384        }
385    }
386
387    /**
388    Creates a new range that matches any version.
389    */
390    pub fn any() -> Self {
391        Self(vec![BoundSet::new(Bound::lower(), Bound::upper()).unwrap()])
392    }
393
394    /**
395    Returns true if `version` is satisfied by this range.
396    */
397    pub fn satisfies(&self, version: &Version) -> bool {
398        for range in &self.0 {
399            if range.satisfies(version) {
400                return true;
401            }
402        }
403
404        false
405    }
406
407    /**
408    Returns true if `other` is a strict superset of this range.
409    */
410    pub fn allows_all(&self, other: &Range) -> bool {
411        for this in &self.0 {
412            for that in &other.0 {
413                if this.allows_all(that) {
414                    return true;
415                }
416            }
417        }
418
419        false
420    }
421
422    /**
423    Returns true if `other` has overlap with this range.
424    */
425    pub fn allows_any(&self, other: &Range) -> bool {
426        for this in &self.0 {
427            for that in &other.0 {
428                if this.allows_any(that) {
429                    return true;
430                }
431            }
432        }
433
434        false
435    }
436
437    /**
438    Returns a new range that is the set-intersection between this range and `other`.
439    */
440    pub fn intersect(&self, other: &Self) -> Option<Self> {
441        let mut sets = Vec::new();
442
443        for lefty in &self.0 {
444            for righty in &other.0 {
445                if let Some(set) = lefty.intersect(righty) {
446                    sets.push(set)
447                }
448            }
449        }
450
451        if sets.is_empty() {
452            None
453        } else {
454            Some(Self(sets))
455        }
456    }
457
458    /**
459    Returns a new range that is the set-difference between this range and `other`.
460    */
461    pub fn difference(&self, other: &Self) -> Option<Self> {
462        let mut predicates = Vec::new();
463
464        for lefty in &self.0 {
465            for righty in &other.0 {
466                if let Some(mut range) = lefty.difference(righty) {
467                    predicates.append(&mut range)
468                }
469            }
470        }
471
472        if predicates.is_empty() {
473            None
474        } else {
475            Some(Self(predicates))
476        }
477    }
478
479    /// Return the highest [Version] in the list that satisfies the range,
480    /// or `None` if none of them do.
481    ///
482    /// ```rust
483    #[doc = include_str!("../examples/max_satisfying.rs")]
484    ///
485    pub fn max_satisfying<'v>(&self, versions: &'v [Version]) -> Option<&'v Version> {
486        let filtered: Vec<_> = versions.iter().filter(|v| self.satisfies(v)).collect();
487
488        filtered.into_iter().max()
489    }
490
491    /// Return the lowest [Version] in the list that satisfies the range,
492    /// or `None` if none of them do.
493    ///
494    /// ```rust
495    #[doc = include_str!("../examples/min_satisfying.rs")]
496    ///
497    pub fn min_satisfying<'v>(&self, versions: &'v [Version]) -> Option<&'v Version> {
498        let filtered: Vec<_> = versions.iter().filter(|v| self.satisfies(v)).collect();
499
500        filtered.into_iter().min()
501    }
502
503    /**
504    Return the lowest [Version] that can possibly match the given range.
505    */
506    pub fn min_version(&self) -> Option<Version> {
507        if let Some(min_bound) = self.0.iter().map(|range| &range.lower).min() {
508            let min_bound = min_bound.as_ref();
509            match min_bound {
510                Bound::Lower(pred) => match pred {
511                    Predicate::Including(v) => Some(v.clone()),
512                    Predicate::Excluding(v) => {
513                        let mut v = v.clone();
514                        if v.is_prerelease() {
515                            v.pre_release.push(Identifier::Numeric(0))
516                        } else {
517                            v.patch += 1;
518                        }
519                        Some(v)
520                    }
521                    Predicate::Unbounded => {
522                        let mut zero = Version::from((0, 0, 0));
523                        if self.satisfies(&zero) {
524                            return Some(zero);
525                        }
526
527                        zero.pre_release.push(Identifier::Numeric(0));
528                        if self.satisfies(&zero) {
529                            return Some(zero);
530                        }
531                        None
532                    }
533                },
534                Bound::Upper(_) => None,
535            }
536        } else {
537            None
538        }
539    }
540}
541
542impl fmt::Display for Range {
543    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
544        for (i, range) in self.0.iter().enumerate() {
545            if i > 0 {
546                write!(f, "||")?;
547            }
548            write!(f, "{}", range)?;
549        }
550        Ok(())
551    }
552}
553
554impl std::str::FromStr for Range {
555    type Err = SemverError;
556    fn from_str(s: &str) -> Result<Self, Self::Err> {
557        Range::parse(s)
558    }
559}
560
561// ---- Parser ----
562
563/*
564Grammar from https://github.com/npm/node-semver#range-grammar
565
566range-set  ::= range ( logical-or range ) *
567logical-or ::= ( ' ' ) * '||' ( ' ' ) *
568range      ::= hyphen | simple ( ' ' simple ) * | ''
569hyphen     ::= partial ' - ' partial
570simple     ::= primitive | partial | tilde | caret
571primitive  ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial
572partial    ::= xr ( '.' xr ( '.' xr qualifier ? )? )?
573xr         ::= 'x' | 'X' | '*' | nr
574nr         ::= '0' | ['1'-'9'] ( ['0'-'9'] ) *
575tilde      ::= '~' partial
576caret      ::= '^' partial
577qualifier  ::= ( '-' pre )? ( '+' build )?
578pre        ::= parts
579build      ::= parts
580parts      ::= part ( '.' part ) *
581part       ::= nr | [-0-9A-Za-z]+
582
583
584Loose mode (all LHS are invalid in strict mode):
585* 01.02.03 -> 1.2.3
586* 1.2.3alpha -> 1.2.3-alpha
587* v 1.2.3 -> 1.2.3 (v1.2.3 is actually a valid "plain" version)
588* =1.2.3 -> 1.2.3 (already a valid range)
589* - 10 -> >=10.0.0 <11.0.0
590* 1.2.3 foo 4.5.6 -> 1.2.3 4.5.6
591* 1.2.3.4 -> invalid range
592* foo -> invalid range
593* 1.2beta4 -> invalid range
594
595TODO: add tests for all these
596*/
597
598// range-set ::= range ( logical-or range ) *
599fn range_set<'s>(input: &mut &'s str) -> PResult<Range, SemverParseError<&'s str>> {
600    Parser::try_map(bound_sets, |sets| {
601        if sets.is_empty() {
602            Err(SemverParseError {
603                input,
604                kind: Some(SemverErrorKind::NoValidRanges),
605                context: None,
606            })
607        } else {
608            Ok(Range(sets))
609        }
610    })
611    .parse_next(input)
612}
613
614// logical-or ::= ( ' ' ) * '||' ( ' ' ) *
615fn bound_sets<'s>(input: &mut &'s str) -> PResult<Vec<BoundSet>, SemverParseError<&'s str>> {
616    Parser::map(
617        separated(0.., range, logical_or),
618        |sets: Vec<Vec<BoundSet>>| sets.into_iter().flatten().collect(),
619    )
620    .parse_next(input)
621}
622fn logical_or<'s>(input: &mut &'s str) -> PResult<(), SemverParseError<&'s str>> {
623    Parser::map(delimited(space0, literal("||"), space0), |_| ()).parse_next(input)
624}
625
626fn range<'s>(input: &mut &'s str) -> PResult<Vec<BoundSet>, SemverParseError<&'s str>> {
627    // TODO: loose parsing means that `1.2.3 foo` translates to `1.2.3`, so we
628    // need to do some stuff here to filter out unwanted BoundSets.
629    Parser::map(
630        separated(0.., simple, space1),
631        |bs: Vec<Option<BoundSet>>| {
632            bs.into_iter()
633                .flatten()
634                .fold(Vec::new(), |mut acc: Vec<BoundSet>, bs| {
635                    if let Some(last) = acc.pop() {
636                        if let Some(bound) = last.intersect(&bs) {
637                            acc.push(bound);
638                        } else {
639                            acc.push(last);
640                            acc.push(bs);
641                        }
642                    } else {
643                        acc.push(bs)
644                    }
645                    acc
646                })
647        },
648    )
649    .parse_next(input)
650}
651
652// simple ::= primitive | partial | tilde | caret | garbage
653fn simple<'s>(input: &mut &'s str) -> PResult<Option<BoundSet>, SemverParseError<&'s str>> {
654    alt((
655        terminated(hyphen, peek(alt((space1, literal("||"), eof)))),
656        terminated(primitive, peek(alt((space1, literal("||"), eof)))),
657        terminated(partial, peek(alt((space1, literal("||"), eof)))),
658        terminated(tilde, peek(alt((space1, literal("||"), eof)))),
659        terminated(caret, peek(alt((space1, literal("||"), eof)))),
660        garbage,
661    ))
662    .parse_next(input)
663}
664
665fn garbage<'s>(input: &mut &'s str) -> PResult<Option<BoundSet>, SemverParseError<&'s str>> {
666    Parser::map(
667        repeat_till(0.., any, alt((peek(space1), peek(literal("||")), eof))),
668        |_: ((), &str)| None,
669    )
670    .parse_next(input)
671}
672
673// primitive  ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial
674fn primitive<'s>(input: &mut &'s str) -> PResult<Option<BoundSet>, SemverParseError<&'s str>> {
675    use Operation::*;
676
677    Parser::map(
678        (operation, preceded(space0, partial_version)),
679        |parsed| match parsed {
680            (GreaterThanEquals, partial) => {
681                BoundSet::at_least(Predicate::Including(partial.into()))
682            }
683            (
684                GreaterThan,
685                Partial {
686                    major: Some(major),
687                    minor: Some(minor),
688                    patch: None,
689                    ..
690                },
691            ) => BoundSet::at_least(Predicate::Including((major, minor + 1, 0).into())),
692            (
693                GreaterThan,
694                Partial {
695                    major: Some(major),
696                    minor: None,
697                    patch: None,
698                    ..
699                },
700            ) => BoundSet::at_least(Predicate::Including((major + 1, 0, 0).into())),
701            (GreaterThan, partial) => BoundSet::at_least(Predicate::Excluding(partial.into())),
702            (
703                LessThan,
704                Partial {
705                    major: Some(major),
706                    minor: Some(minor),
707                    patch: None,
708                    ..
709                },
710            ) => BoundSet::at_most(Predicate::Excluding((major, minor, 0, 0).into())),
711            (
712                LessThan,
713                Partial {
714                    major,
715                    minor,
716                    patch,
717                    pre_release,
718                    build,
719                    ..
720                },
721            ) => BoundSet::at_most(Predicate::Excluding(Version {
722                major: major.unwrap_or(0),
723                minor: minor.unwrap_or(0),
724                patch: patch.unwrap_or(0),
725                build,
726                pre_release,
727            })),
728            (
729                LessThanEquals,
730                Partial {
731                    major,
732                    minor: None,
733                    patch: None,
734                    ..
735                },
736            ) => BoundSet::at_most(Predicate::Including(
737                (major.unwrap_or(0), MAX_SAFE_INTEGER, MAX_SAFE_INTEGER).into(),
738            )),
739            (
740                LessThanEquals,
741                Partial {
742                    major,
743                    minor,
744                    patch: None,
745                    ..
746                },
747            ) => BoundSet::at_most(Predicate::Including(
748                (major.unwrap_or(0), minor.unwrap_or(0), MAX_SAFE_INTEGER).into(),
749            )),
750            (LessThanEquals, partial) => BoundSet::at_most(Predicate::Including(partial.into())),
751            (
752                Exact,
753                Partial {
754                    major: Some(major),
755                    minor: Some(minor),
756                    patch: Some(patch),
757                    pre_release,
758                    ..
759                },
760            ) => BoundSet::exact(Version {
761                major,
762                minor,
763                patch,
764                pre_release,
765                build: vec![],
766            }),
767            (
768                Exact,
769                Partial {
770                    major: Some(major),
771                    minor: Some(minor),
772                    ..
773                },
774            ) => BoundSet::new(
775                Bound::Lower(Predicate::Including((major, minor, 0).into())),
776                Bound::Upper(Predicate::Excluding(Version {
777                    major,
778                    minor: minor + 1,
779                    patch: 0,
780                    pre_release: vec![Identifier::Numeric(0)],
781                    build: vec![],
782                })),
783            ),
784            (
785                Exact,
786                Partial {
787                    major: Some(major), ..
788                },
789            ) => BoundSet::new(
790                Bound::Lower(Predicate::Including((major, 0, 0).into())),
791                Bound::Upper(Predicate::Excluding(Version {
792                    major: major + 1,
793                    minor: 0,
794                    patch: 0,
795                    pre_release: vec![Identifier::Numeric(0)],
796                    build: vec![],
797                })),
798            ),
799            _ => None,
800        },
801    )
802    .context("operation range (ex: >= 1.2.3)")
803    .parse_next(input)
804}
805
806fn operation<'s>(input: &mut &'s str) -> PResult<Operation, SemverParseError<&'s str>> {
807    use Operation::*;
808    alt((
809        Parser::map(literal(">="), |_| GreaterThanEquals),
810        Parser::map(literal(">"), |_| GreaterThan),
811        Parser::map(literal("="), |_| Exact),
812        Parser::map(literal("<="), |_| LessThanEquals),
813        Parser::map(literal("<"), |_| LessThan),
814    ))
815    .parse_next(input)
816}
817
818fn partial<'s>(input: &mut &'s str) -> PResult<Option<BoundSet>, SemverParseError<&'s str>> {
819    Parser::map(partial_version, |partial| match partial {
820        Partial { major: None, .. } => BoundSet::at_least(Predicate::Including((0, 0, 0).into())),
821        Partial {
822            major: Some(major),
823            minor: None,
824            ..
825        } => BoundSet::new(
826            Bound::Lower(Predicate::Including((major, 0, 0).into())),
827            Bound::Upper(Predicate::Excluding(Version {
828                major: major + 1,
829                minor: 0,
830                patch: 0,
831                pre_release: vec![Identifier::Numeric(0)],
832                build: vec![],
833            })),
834        ),
835        Partial {
836            major: Some(major),
837            minor: Some(minor),
838            patch: None,
839            ..
840        } => BoundSet::new(
841            Bound::Lower(Predicate::Including((major, minor, 0).into())),
842            Bound::Upper(Predicate::Excluding(Version {
843                major,
844                minor: minor + 1,
845                patch: 0,
846                pre_release: vec![Identifier::Numeric(0)],
847                build: vec![],
848            })),
849        ),
850        partial => BoundSet::exact(partial.into()),
851    })
852    .context("plain version range (ex: 1.2)")
853    .parse_next(input)
854}
855
856#[derive(Debug, Clone)]
857struct Partial {
858    major: Option<u64>,
859    minor: Option<u64>,
860    patch: Option<u64>,
861    pre_release: Vec<Identifier>,
862    build: Vec<Identifier>,
863}
864
865impl From<Partial> for Version {
866    fn from(partial: Partial) -> Self {
867        Version {
868            major: partial.major.unwrap_or(0),
869            minor: partial.minor.unwrap_or(0),
870            patch: partial.patch.unwrap_or(0),
871            pre_release: partial.pre_release,
872            build: partial.build,
873        }
874    }
875}
876
877// partial ::= xr ( '.' xr ( '.' xr qualifier ? )? )?
878// xr      ::= 'x' | 'X' | '*' | nr
879// nr      ::= '0' | ['1'-'9'] ( ['0'-'9'] ) *
880// NOTE: Loose mode means nr is actually just `['0'-'9']`.
881fn partial_version<'s>(input: &mut &'s str) -> PResult<Partial, SemverParseError<&'s str>> {
882    let _ = opt(literal("v")).parse_next(input)?;
883    let _ = space0(input)?;
884    let major = component(input)?;
885    let minor = opt(preceded(literal("."), component)).parse_next(input)?;
886    let patch = opt(preceded(literal("."), component)).parse_next(input)?;
887    let (pre, build) = if patch.is_some() {
888        extras(input)?
889    } else {
890        (vec![], vec![])
891    };
892    Ok(Partial {
893        major,
894        minor: minor.flatten(),
895        patch: patch.flatten(),
896        pre_release: pre,
897        build,
898    })
899}
900
901fn component<'s>(input: &mut &'s str) -> PResult<Option<u64>, SemverParseError<&'s str>> {
902    alt((
903        Parser::map(x_or_asterisk, |_| None),
904        Parser::map(number, Some),
905    ))
906    .parse_next(input)
907}
908
909fn x_or_asterisk<'s>(input: &mut &'s str) -> PResult<(), SemverParseError<&'s str>> {
910    Parser::map(alt((literal("x"), literal("X"), literal("*"))), |_| ()).parse_next(input)
911}
912
913fn tilde_gt<'s>(input: &mut &'s str) -> PResult<Option<&'s str>, SemverParseError<&'s str>> {
914    Parser::map(
915        (literal("~"), space0, opt(literal(">")), space0),
916        |(_, _, gt, _)| gt,
917    )
918    .parse_next(input)
919}
920
921fn tilde<'s>(input: &mut &'s str) -> PResult<Option<BoundSet>, SemverParseError<&'s str>> {
922    Parser::map((tilde_gt, partial_version), |parsed| match parsed {
923        (
924            Some(_gt),
925            Partial {
926                major: Some(major),
927                minor: None,
928                patch: None,
929                ..
930            },
931        ) => BoundSet::new(
932            Bound::Lower(Predicate::Including((major, 0, 0).into())),
933            Bound::Upper(Predicate::Excluding((major + 1, 0, 0, 0).into())),
934        ),
935        (
936            Some(_gt),
937            Partial {
938                major: Some(major),
939                minor: Some(minor),
940                patch,
941                pre_release,
942                ..
943            },
944        ) => BoundSet::new(
945            Bound::Lower(Predicate::Including(Version {
946                major,
947                minor,
948                patch: patch.unwrap_or(0),
949                pre_release,
950                build: vec![],
951            })),
952            Bound::Upper(Predicate::Excluding((major, minor + 1, 0, 0).into())),
953        ),
954        (
955            None,
956            Partial {
957                major: Some(major),
958                minor: Some(minor),
959                patch: Some(patch),
960                pre_release,
961                ..
962            },
963        ) => BoundSet::new(
964            Bound::Lower(Predicate::Including(Version {
965                major,
966                minor,
967                patch,
968                pre_release,
969                build: vec![],
970            })),
971            Bound::Upper(Predicate::Excluding((major, minor + 1, 0, 0).into())),
972        ),
973        (
974            None,
975            Partial {
976                major: Some(major),
977                minor: Some(minor),
978                patch: None,
979                ..
980            },
981        ) => BoundSet::new(
982            Bound::Lower(Predicate::Including((major, minor, 0).into())),
983            Bound::Upper(Predicate::Excluding((major, minor + 1, 0, 0).into())),
984        ),
985        (
986            None,
987            Partial {
988                major: Some(major),
989                minor: None,
990                patch: None,
991                ..
992            },
993        ) => BoundSet::new(
994            Bound::Lower(Predicate::Including((major, 0, 0).into())),
995            Bound::Upper(Predicate::Excluding((major + 1, 0, 0, 0).into())),
996        ),
997        _ => None,
998    })
999    .context("tilde version range (ex: ~1.2.3)")
1000    .parse_next(input)
1001}
1002
1003fn caret<'s>(input: &mut &'s str) -> PResult<Option<BoundSet>, SemverParseError<&'s str>> {
1004    Parser::map(
1005        preceded((literal("^"), space0), partial_version),
1006        |parsed| match parsed {
1007            Partial {
1008                major: Some(0),
1009                minor: None,
1010                patch: None,
1011                ..
1012            } => BoundSet::at_most(Predicate::Excluding((1, 0, 0, 0).into())),
1013            Partial {
1014                major: Some(0),
1015                minor: Some(minor),
1016                patch: None,
1017                ..
1018            } => BoundSet::new(
1019                Bound::Lower(Predicate::Including((0, minor, 0).into())),
1020                Bound::Upper(Predicate::Excluding((0, minor + 1, 0, 0).into())),
1021            ),
1022            // TODO: can be compressed?
1023            Partial {
1024                major: Some(major),
1025                minor: None,
1026                patch: None,
1027                ..
1028            } => BoundSet::new(
1029                Bound::Lower(Predicate::Including((major, 0, 0).into())),
1030                Bound::Upper(Predicate::Excluding((major + 1, 0, 0, 0).into())),
1031            ),
1032            Partial {
1033                major: Some(major),
1034                minor: Some(minor),
1035                patch: None,
1036                ..
1037            } => BoundSet::new(
1038                Bound::Lower(Predicate::Including((major, minor, 0).into())),
1039                Bound::Upper(Predicate::Excluding((major + 1, 0, 0, 0).into())),
1040            ),
1041            Partial {
1042                major: Some(major),
1043                minor: Some(minor),
1044                patch: Some(patch),
1045                pre_release,
1046                ..
1047            } => BoundSet::new(
1048                Bound::Lower(Predicate::Including(Version {
1049                    major,
1050                    minor,
1051                    patch,
1052                    pre_release,
1053                    build: vec![],
1054                })),
1055                Bound::Upper(Predicate::Excluding(match (major, minor, patch) {
1056                    (0, 0, n) => Version::from((0, 0, n + 1, 0)),
1057                    (0, n, _) => Version::from((0, n + 1, 0, 0)),
1058                    (n, _, _) => Version::from((n + 1, 0, 0, 0)),
1059                })),
1060            ),
1061            _ => None,
1062        },
1063    )
1064    .context("caret version range (ex: ^1.2.3)")
1065    .parse_next(input)
1066}
1067
1068// hyphen ::= ' - ' partial /* loose */ | partial ' - ' partial
1069fn hyphen<'s>(input: &mut &'s str) -> PResult<Option<BoundSet>, SemverParseError<&'s str>> {
1070    fn parser<'s>(input: &mut &'s str) -> PResult<Option<BoundSet>, SemverParseError<&'s str>> {
1071        let lower = opt(partial_version).parse_next(input)?;
1072        let _ = space1(input)?;
1073        let _ = literal("-").parse_next(input)?;
1074        let _ = space1(input)?;
1075        let upper = partial_version(input)?;
1076        let upper = match upper {
1077            Partial {
1078                major: None,
1079                minor: None,
1080                patch: None,
1081                ..
1082            } => Predicate::Excluding(Version {
1083                major: 0,
1084                minor: 0,
1085                patch: 0,
1086                pre_release: vec![Identifier::Numeric(0)],
1087                build: vec![],
1088            }),
1089            Partial {
1090                major: Some(major),
1091                minor: None,
1092                patch: None,
1093                ..
1094            } => Predicate::Excluding(Version {
1095                major: major + 1,
1096                minor: 0,
1097                patch: 0,
1098                pre_release: vec![Identifier::Numeric(0)],
1099                build: vec![],
1100            }),
1101            Partial {
1102                major: Some(major),
1103                minor: Some(minor),
1104                patch: None,
1105                ..
1106            } => Predicate::Excluding(Version {
1107                major,
1108                minor: minor + 1,
1109                patch: 0,
1110                pre_release: vec![Identifier::Numeric(0)],
1111                build: vec![],
1112            }),
1113            partial => Predicate::Including(partial.into()),
1114        };
1115        let bounds = if let Some(lower) = lower {
1116            BoundSet::new(
1117                Bound::Lower(Predicate::Including(lower.into())),
1118                Bound::Upper(upper),
1119            )
1120        } else {
1121            BoundSet::at_most(upper)
1122        };
1123        Ok(bounds)
1124    }
1125
1126    parser
1127        .context("hyphenated version range (ex: 1.2 - 2)")
1128        .parse_next(input)
1129}
1130
1131macro_rules! create_tests_for {
1132    ($func:ident $($name:ident => $version_range:expr , { $x:ident => $allows:expr, $y:ident => $denies:expr$(,)? }),+ ,$(,)?) => {
1133
1134        #[cfg(test)]
1135        mod $func {
1136        use super::*;
1137
1138            $(
1139                #[test]
1140                fn $name() {
1141                    let version_range = Range::parse($version_range).unwrap();
1142
1143                    let allows: Vec<Range> = $allows.iter().map(|v| Range::parse(v).unwrap()).collect();
1144                    for version in &allows {
1145                        assert!(version_range.$func(version), "should have allowed: {}", version);
1146                    }
1147
1148                    let ranges: Vec<Range> = $denies.iter().map(|v| Range::parse(v).unwrap()).collect();
1149                    for version in &ranges {
1150                        assert!(!version_range.$func(version), "should have denied: {}", version);
1151                    }
1152                }
1153            )+
1154        }
1155    }
1156}
1157
1158create_tests_for! {
1159    // The function we are testing:
1160    allows_all
1161
1162    greater_than_eq_123   => ">=1.2.3", {
1163        allows => [">=2.0.0", ">2", "2.0.0", "0.1 || 1.4", "1.2.3", "2 - 7", ">2.0.0"],
1164        denies => ["1.0.0", "<1.2", ">=1.2.2", "1 - 3", "0.1 || <1.2.0", ">1.0.0"],
1165    },
1166
1167    greater_than_123      => ">1.2.3", {
1168        allows => [">=2.0.0", ">2", "2.0.0", "0.1 || 1.4", ">2.0.0"],
1169        denies => ["1.0.0", "<1.2", ">=1.2.3", "1 - 3", "0.1 || <1.2.0", "<=3"],
1170    },
1171
1172    eq_123  => "1.2.3", {
1173        allows => ["1.2.3"],
1174        denies => ["1.0.0", "<1.2", "1.x", ">=1.2.2", "1 - 3", "0.1 || <1.2.0"],
1175    },
1176
1177    lt_123  => "<1.2.3", {
1178        allows => ["<=1.2.0", "<1", "1.0.0", "0.1 || 1.4"],
1179        denies => ["1 - 3", ">1", "2.0.0", "2.0 || >9", ">1.0.0"],
1180    },
1181
1182    lt_eq_123 => "<=1.2.3", {
1183        allows => ["<=1.2.0", "<1", "1.0.0", "0.1 || 1.4", "1.2.3"],
1184        denies => ["1 - 3", ">1.0.0", ">=1.0.0"],
1185    },
1186
1187    eq_123_or_gt_400  => "1.2.3 || >4", {
1188        allows => [ "1.2.3", ">4", "5.x", "5.2.x", ">=8.2.1", "2.0 || 5.6.7"],
1189        denies => ["<2", "1 - 7", "1.9.4 || 2 - 3"],
1190    },
1191
1192    between_two_and_eight => "2 - 8", {
1193        allows => [ "2.2.3", "4 - 5"],
1194        denies => ["1 - 4", "5 - 9", ">3", "<=5"],
1195    },
1196}
1197
1198create_tests_for! {
1199    // The function we are testing:
1200    allows_any
1201
1202    greater_than_eq_123   => ">=1.2.3", {
1203        allows => ["<=1.2.4", "3.0.0", "<2", ">=3", ">3.0.0"],
1204        denies => ["<=1.2.0", "1.0.0", "<1"],
1205    },
1206
1207    greater_than_123   => ">1.2.3", {
1208        allows => ["<=1.2.4", "3.0.0", "<2", ">=3", ">3.0.0"],
1209        denies => ["<=1.2.3", "1.0.0", "<1"],
1210    },
1211
1212    eq_123   => "1.2.3", {
1213        allows => ["1.2.3", "1 - 2"],
1214        denies => ["<1.2.3", "1.0.0", ">4.5.6", ">5"],
1215    },
1216
1217    lt_eq_123  => "<=1.2.3", {
1218        allows => ["<=1.2.0", "<1.0.0", "1.0.0", ">1.0.0", ">=1.2.0"],
1219        denies => ["4.5.6", ">2.0.0", ">=2.0.0"],
1220    },
1221
1222    lt_123  => "<1.2.3", {
1223        allows => ["<=2.2.0", "<2.0.0", "1.0.0", ">1.0.0", ">=1.2.0"],
1224        denies => ["2.0.0", ">1.8.0", ">=1.8.0"],
1225    },
1226
1227    between_two_and_eight => "2 - 8", {
1228        allows => ["2.2.3", "4 - 10", ">4", ">4.0.0", "<=4.0.0", "<9.1.2"],
1229        denies => [">10", "10 - 11", "0 - 1"],
1230    },
1231
1232    eq_123_or_gt_400  => "1.2.3 || >4", {
1233        allows => [ "1.2.3", ">3", "5.x", "5.2.x", ">=8.2.1", "2 - 7", "2.0 || 5.6.7"],
1234        denies => [ "1.9.4 || 2 - 3"],
1235    },
1236}
1237
1238#[cfg(test)]
1239mod intersection {
1240    use super::*;
1241
1242    fn v(range: &'static str) -> Range {
1243        range.parse().unwrap()
1244    }
1245
1246    #[test]
1247    fn gt_eq_123() {
1248        let base_range = v(">=1.2.3");
1249
1250        let samples = vec![
1251            ("<=2.0.0", Some(">=1.2.3 <=2.0.0")),
1252            ("<2.0.0", Some(">=1.2.3 <2.0.0")),
1253            (">=2.0.0", Some(">=2.0.0")),
1254            (">2.0.0", Some(">2.0.0")),
1255            (">1.0.0", Some(">=1.2.3")),
1256            (">1.2.3", Some(">1.2.3")),
1257            ("<=1.2.3", Some("1.2.3")),
1258            ("2.0.0", Some("2.0.0")),
1259            ("1.1.1", None),
1260            ("<1.0.0", None),
1261        ];
1262
1263        assert_ranges_match(base_range, samples);
1264    }
1265
1266    #[test]
1267    fn gt_123() {
1268        let base_range = v(">1.2.3");
1269
1270        let samples = vec![
1271            ("<=2.0.0", Some(">1.2.3 <=2.0.0")),
1272            ("<2.0.0", Some(">1.2.3 <2.0.0")),
1273            (">=2.0.0", Some(">=2.0.0")),
1274            (">2.0.0", Some(">2.0.0")),
1275            ("2.0.0", Some("2.0.0")),
1276            (">1.2.3", Some(">1.2.3")),
1277            ("<=1.2.3", None),
1278            ("1.1.1", None),
1279            ("<1.0.0", None),
1280        ];
1281
1282        assert_ranges_match(base_range, samples);
1283    }
1284
1285    #[test]
1286    fn eq_123() {
1287        let base_range = v("1.2.3");
1288
1289        let samples = vec![
1290            ("<=2.0.0", Some("1.2.3")),
1291            ("<2.0.0", Some("1.2.3")),
1292            (">=2.0.0", None),
1293            (">2.0.0", None),
1294            ("2.0.0", None),
1295            ("1.2.3", Some("1.2.3")),
1296            (">1.2.3", None),
1297            ("<=1.2.3", Some("1.2.3")),
1298            ("1.1.1", None),
1299            ("<1.0.0", None),
1300        ];
1301
1302        assert_ranges_match(base_range, samples);
1303    }
1304
1305    #[test]
1306    fn lt_123() {
1307        let base_range = v("<1.2.3");
1308
1309        let samples = vec![
1310            ("<=2.0.0", Some("<1.2.3")),
1311            ("<2.0.0", Some("<1.2.3")),
1312            (">=2.0.0", None),
1313            (">=1.0.0", Some(">=1.0.0 <1.2.3")),
1314            (">2.0.0", None),
1315            ("2.0.0", None),
1316            ("1.2.3", None),
1317            (">1.2.3", None),
1318            ("<=1.2.3", Some("<1.2.3")),
1319            ("1.1.1", Some("1.1.1")),
1320            ("<1.0.0", Some("<1.0.0")),
1321        ];
1322
1323        assert_ranges_match(base_range, samples);
1324    }
1325
1326    #[test]
1327    fn lt_eq_123() {
1328        let base_range = v("<=1.2.3");
1329
1330        let samples = vec![
1331            ("<=2.0.0", Some("<=1.2.3")),
1332            ("<2.0.0", Some("<=1.2.3")),
1333            (">=2.0.0", None),
1334            (">=1.0.0", Some(">=1.0.0 <=1.2.3")),
1335            (">2.0.0", None),
1336            ("2.0.0", None),
1337            ("1.2.3", Some("1.2.3")),
1338            (">1.2.3", None),
1339            ("<=1.2.3", Some("<=1.2.3")),
1340            ("1.1.1", Some("1.1.1")),
1341            ("<1.0.0", Some("<1.0.0")),
1342        ];
1343
1344        assert_ranges_match(base_range, samples);
1345    }
1346
1347    #[test]
1348    fn multiple() {
1349        let base_range = v("<1 || 3 - 4");
1350
1351        let samples = vec![("0.5 - 3.5.0", Some(">=0.5.0 <1.0.0||>=3.0.0 <=3.5.0"))];
1352
1353        assert_ranges_match(base_range, samples);
1354    }
1355
1356    fn assert_ranges_match(base: Range, samples: Vec<(&'static str, Option<&'static str>)>) {
1357        for (other, expected) in samples {
1358            let other = v(other);
1359            let resulting_range = base.intersect(&other).map(|v| v.to_string());
1360            assert_eq!(
1361                resulting_range.clone(),
1362                expected.map(|e| e.to_string()),
1363                "{} ∩ {} := {}",
1364                base,
1365                other,
1366                resulting_range.unwrap_or_else(|| "⊗".into())
1367            );
1368        }
1369    }
1370}
1371
1372#[cfg(test)]
1373mod difference {
1374    use super::*;
1375
1376    fn v(range: &'static str) -> Range {
1377        range.parse().unwrap()
1378    }
1379
1380    #[test]
1381    fn gt_eq_123() {
1382        let base_range = v(">=1.2.3");
1383
1384        let samples = vec![
1385            ("<=2.0.0", Some(">2.0.0")),
1386            ("<2.0.0", Some(">=2.0.0")),
1387            (">=2.0.0", Some(">=1.2.3 <2.0.0")),
1388            (">2.0.0", Some(">=1.2.3 <=2.0.0")),
1389            (">1.0.0", None),
1390            (">1.2.3", Some("1.2.3")),
1391            ("<=1.2.3", Some(">1.2.3")),
1392            ("1.1.1", Some(">=1.2.3")),
1393            ("<1.0.0", Some(">=1.2.3")),
1394            ("2.0.0", Some(">=1.2.3 <2.0.0||>2.0.0")),
1395        ];
1396
1397        assert_ranges_match(base_range, samples);
1398    }
1399
1400    #[test]
1401    fn gt_123() {
1402        let base_range = v(">1.2.3");
1403
1404        let samples = vec![
1405            ("<=2.0.0", Some(">2.0.0")),
1406            ("<2.0.0", Some(">=2.0.0")),
1407            (">=2.0.0", Some(">1.2.3 <2.0.0")),
1408            (">2.0.0", Some(">1.2.3 <=2.0.0")),
1409            (">1.0.0", None),
1410            (">1.2.3", None),
1411            ("<=1.2.3", Some(">1.2.3")),
1412            ("1.1.1", Some(">1.2.3")),
1413            ("<1.0.0", Some(">1.2.3")),
1414            ("2.0.0", Some(">1.2.3 <2.0.0||>2.0.0")),
1415        ];
1416
1417        assert_ranges_match(base_range, samples);
1418    }
1419
1420    #[test]
1421    fn eq_123() {
1422        let base_range = v("1.2.3");
1423
1424        let samples = vec![
1425            ("<=2.0.0", None),
1426            ("<2.0.0", None),
1427            (">=2.0.0", Some("1.2.3")),
1428            (">2.0.0", Some("1.2.3")),
1429            (">1.0.0", None),
1430            (">1.2.3", Some("1.2.3")),
1431            ("1.2.3", None),
1432            ("<=1.2.3", None),
1433            ("1.1.1", Some("1.2.3")),
1434            ("<1.0.0", Some("1.2.3")),
1435            ("2.0.0", Some("1.2.3")),
1436        ];
1437
1438        assert_ranges_match(base_range, samples);
1439    }
1440
1441    #[test]
1442    fn lt_123() {
1443        let base_range = v("<1.2.3");
1444
1445        let samples = vec![
1446            ("<=2.0.0", None),
1447            ("<2.0.0", None),
1448            (">=2.0.0", Some("<1.2.3")),
1449            (">2.0.0", Some("<1.2.3")),
1450            (">1.0.0", Some("<=1.0.0")),
1451            (">1.2.3", Some("<1.2.3")),
1452            ("<=1.2.3", None),
1453            ("1.1.1", Some("<1.1.1||>1.1.1 <1.2.3")),
1454            ("<1.0.0", Some(">=1.0.0 <1.2.3")),
1455            ("2.0.0", Some("<1.2.3")),
1456        ];
1457
1458        assert_ranges_match(base_range, samples);
1459    }
1460
1461    #[test]
1462    fn lt_eq_123() {
1463        let base_range = v("<=1.2.3");
1464
1465        let samples = vec![
1466            ("<=2.0.0", None),
1467            ("<2.0.0", None),
1468            (">=2.0.0", Some("<=1.2.3")),
1469            (">2.0.0", Some("<=1.2.3")),
1470            (">1.0.0", Some("<=1.0.0")),
1471            (">1.2.3", Some("<=1.2.3")),
1472            ("<=1.2.3", None),
1473            ("1.1.1", Some("<1.1.1||>1.1.1 <=1.2.3")),
1474            ("<1.0.0", Some(">=1.0.0 <=1.2.3")),
1475            ("2.0.0", Some("<=1.2.3")),
1476        ];
1477
1478        assert_ranges_match(base_range, samples);
1479    }
1480
1481    #[test]
1482    fn multiple() {
1483        let base_range = v("<1 || 3 - 4");
1484
1485        let samples = vec![("0.5 - 3.5.0", Some("<0.5.0||>3.5.0 <5.0.0-0"))];
1486
1487        assert_ranges_match(base_range, samples);
1488    }
1489
1490    fn assert_ranges_match(base: Range, samples: Vec<(&'static str, Option<&'static str>)>) {
1491        for (other, expected) in samples {
1492            let other = v(other);
1493            let resulting_range = base.difference(&other).map(|v| v.to_string());
1494            assert_eq!(
1495                resulting_range.clone(),
1496                expected.map(|e| e.to_string()),
1497                "{} \\ {} := {}",
1498                base,
1499                other,
1500                resulting_range.unwrap_or_else(|| "⊗".into())
1501            );
1502        }
1503    }
1504}
1505
1506#[cfg(test)]
1507mod satisfies_ranges_tests {
1508    use super::*;
1509
1510    macro_rules! refute {
1511        ($e:expr) => {
1512            assert!(!$e)
1513        };
1514        ($e:expr, $msg:expr) => {
1515            assert!(!$e, $msg)
1516        };
1517    }
1518
1519    #[test]
1520    fn greater_than_equals() {
1521        let parsed = Range::parse(">=1.2.3").expect("unable to parse");
1522
1523        refute!(parsed.satisfies(&(0, 2, 3).into()), "major too low");
1524        refute!(parsed.satisfies(&(1, 1, 3).into()), "minor too low");
1525        refute!(parsed.satisfies(&(1, 2, 2).into()), "patch too low");
1526        assert!(parsed.satisfies(&(1, 2, 3).into()), "exact");
1527        assert!(parsed.satisfies(&(2, 2, 3).into()), "above");
1528    }
1529
1530    #[test]
1531    fn greater_than() {
1532        let parsed = Range::parse(">1.2.3").expect("unable to parse");
1533
1534        refute!(parsed.satisfies(&(0, 2, 3).into()), "major too low");
1535        refute!(parsed.satisfies(&(1, 1, 3).into()), "minor too low");
1536        refute!(parsed.satisfies(&(1, 2, 2).into()), "patch too low");
1537        refute!(parsed.satisfies(&(1, 2, 3).into()), "exact");
1538        assert!(parsed.satisfies(&(1, 2, 4).into()), "above");
1539    }
1540
1541    #[test]
1542    fn exact() {
1543        let parsed = Range::parse("=1.2.3").expect("unable to parse");
1544
1545        refute!(parsed.satisfies(&(1, 2, 2).into()), "patch too low");
1546        assert!(parsed.satisfies(&(1, 2, 3).into()), "exact");
1547        refute!(parsed.satisfies(&(1, 2, 4).into()), "above");
1548    }
1549
1550    #[test]
1551    fn less_than() {
1552        let parsed = Range::parse("<1.2.3").expect("unable to parse");
1553
1554        assert!(parsed.satisfies(&(0, 2, 3).into()), "major below");
1555        assert!(parsed.satisfies(&(1, 1, 3).into()), "minor below");
1556        assert!(parsed.satisfies(&(1, 2, 2).into()), "patch below");
1557        refute!(parsed.satisfies(&(1, 2, 3).into()), "exact");
1558        refute!(parsed.satisfies(&(1, 2, 4).into()), "above");
1559    }
1560
1561    #[test]
1562    fn less_than_equals() {
1563        let parsed = Range::parse("<=1.2.3").expect("unable to parse");
1564
1565        assert!(parsed.satisfies(&(0, 2, 3).into()), "major below");
1566        assert!(parsed.satisfies(&(1, 1, 3).into()), "minor below");
1567        assert!(parsed.satisfies(&(1, 2, 2).into()), "patch below");
1568        assert!(parsed.satisfies(&(1, 2, 3).into()), "exact");
1569        refute!(parsed.satisfies(&(1, 2, 4).into()), "above");
1570    }
1571
1572    #[test]
1573    fn less_than_equals_major() {
1574        let parsed = Range::parse("<=1").expect("unable to parse");
1575
1576        assert!(parsed.satisfies(&(0, 2, 3).into()), "major below");
1577        assert!(parsed.satisfies(&(1, 1, 3).into()), "minor below");
1578        assert!(parsed.satisfies(&(1, 2, 2).into()), "minor below");
1579        assert!(parsed.satisfies(&(1, 2, 3).into()), "minor below");
1580        assert!(parsed.satisfies(&(1, 2, 4).into()), "minor below");
1581        refute!(parsed.satisfies(&(2, 0, 0).into()), "above");
1582    }
1583
1584    #[test]
1585    fn less_than_equals_minor() {
1586        let parsed = Range::parse("<=1.2").expect("unable to parse");
1587
1588        assert!(parsed.satisfies(&(0, 2, 3).into()), "major below");
1589        assert!(parsed.satisfies(&(1, 1, 3).into()), "minor below");
1590        assert!(parsed.satisfies(&(1, 2, 1).into()), "patch below");
1591        assert!(parsed.satisfies(&(1, 2, 5).into()), "patch below");
1592        refute!(parsed.satisfies(&(1, 3, 0).into()), "above");
1593    }
1594
1595    #[test]
1596    fn only_major() {
1597        let parsed = Range::parse("1").expect("unable to parse");
1598
1599        refute!(parsed.satisfies(&(0, 2, 3).into()), "major below");
1600        assert!(parsed.satisfies(&(1, 0, 0).into()), "exact bottom of range");
1601        assert!(parsed.satisfies(&(1, 2, 2).into()), "middle");
1602        refute!(parsed.satisfies(&(2, 0, 0).into()), "exact top of range");
1603        refute!(parsed.satisfies(&(2, 7, 3).into()), "above");
1604    }
1605
1606    #[test]
1607    fn pre_release_version() {
1608        let range = Range::parse("^2").unwrap();
1609
1610        refute!(
1611            range.satisfies(&Version::parse("2.0.0-alpha.0").unwrap()),
1612            "below"
1613        );
1614        refute!(
1615            range.satisfies(&Version::parse("2.1.0-alpha.0").unwrap()),
1616            "above but pre-release"
1617        );
1618    }
1619
1620    #[test]
1621    fn pre_release_range() {
1622        let range = Range::parse("^1.2.3-rc.4").unwrap();
1623
1624        refute!(range.satisfies(&Version::parse("1.2.2").unwrap()), "below");
1625        assert!(
1626            range.satisfies(&Version::parse("1.2.3").unwrap()),
1627            "equal non-prerelease"
1628        );
1629        assert!(range.satisfies(&Version::parse("1.2.4").unwrap()), "above");
1630    }
1631
1632    #[test]
1633    fn pre_release_version_and_range() {
1634        let range = Range::parse("^1.2.3-rc.4").unwrap();
1635
1636        refute!(
1637            range.satisfies(&Version::parse("1.2.3-rc.3").unwrap()),
1638            "below"
1639        );
1640        assert!(
1641            range.satisfies(&Version::parse("1.2.3-rc.4").unwrap()),
1642            "equal"
1643        );
1644        assert!(
1645            range.satisfies(&Version::parse("1.2.3-rc.5").unwrap()),
1646            "above"
1647        );
1648        refute!(
1649            range.satisfies(&Version::parse("1.2.4-rc.6").unwrap()),
1650            "above patch but pre-release"
1651        );
1652    }
1653}
1654
1655/// https://github.com/npm/node-semver/blob/master/test/fixtures/range-parse.js
1656#[cfg(test)]
1657mod tests {
1658    use super::*;
1659
1660    use pretty_assertions::assert_eq;
1661
1662    macro_rules! range_parse_tests {
1663        ($($name:ident => $vals:expr),+ ,$(,)?) => {
1664            $(
1665                #[test]
1666                fn $name() {
1667                    let [input, expected] = $vals;
1668
1669                    let parsed = Range::parse(input).expect("unable to parse");
1670
1671                    assert_eq!(expected, parsed.to_string());
1672                }
1673            )+
1674        }
1675
1676    }
1677
1678    range_parse_tests![
1679        //       [input,   parsed and then `to_string`ed]
1680        exact => ["1.0.0", "1.0.0"],
1681        major_minor_patch_range => ["1.0.0 - 2.0.0", ">=1.0.0 <=2.0.0"],
1682        only_major_versions =>  ["1 - 2", ">=1.0.0 <3.0.0-0"],
1683        only_major_and_minor => ["1.0 - 2.0", ">=1.0.0 <2.1.0-0"],
1684        mixed_major_minor => ["1.2 - 3.4.5", ">=1.2.0 <=3.4.5"],
1685        mixed_major_minor_2 => ["1.2.3 - 3.4", ">=1.2.3 <3.5.0-0"],
1686        minor_minor_range => ["1.2 - 3.4", ">=1.2.0 <3.5.0-0"],
1687        single_sided_only_major => ["1", ">=1.0.0 <2.0.0-0"],
1688        single_sided_lower_equals_bound =>  [">=1.0.0", ">=1.0.0"],
1689        single_sided_lower_equals_bound_2 => [">=0.1.97", ">=0.1.97"],
1690        single_sided_lower_bound => [">1.0.0", ">1.0.0"],
1691        single_sided_upper_equals_bound => ["<=2.0.0", "<=2.0.0"],
1692        single_sided_upper_equals_bound_with_minor => ["<=2.0", "<=2.0.900719925474099"],
1693        single_sided_upper_bound => ["<2.0.0", "<2.0.0"],
1694        major_and_minor => ["2.3", ">=2.3.0 <2.4.0-0"],
1695        major_dot_x => ["2.x", ">=2.0.0 <3.0.0-0"],
1696        x_and_asterisk_version => ["2.x.x", ">=2.0.0 <3.0.0-0"],
1697        patch_x => ["1.2.x", ">=1.2.0 <1.3.0-0"],
1698        minor_asterisk_patch_asterisk => ["2.*.*", ">=2.0.0 <3.0.0-0"],
1699        patch_asterisk => ["1.2.*", ">=1.2.0 <1.3.0-0"],
1700        caret_zero => ["^0", "<1.0.0-0"],
1701        caret_zero_minor => ["^0.1", ">=0.1.0 <0.2.0-0"],
1702        caret_one => ["^1.0", ">=1.0.0 <2.0.0-0"],
1703        caret_minor => ["^1.2", ">=1.2.0 <2.0.0-0"],
1704        caret_patch => ["^0.0.1", ">=0.0.1 <0.0.2-0"],
1705        caret_with_patch =>   ["^0.1.2", ">=0.1.2 <0.2.0-0"],
1706        caret_with_patch_2 => ["^1.2.3", ">=1.2.3 <2.0.0-0"],
1707        tilde_one => ["~1", ">=1.0.0 <2.0.0-0"],
1708        tilde_minor => ["~1.0", ">=1.0.0 <1.1.0-0"],
1709        tilde_minor_2 => ["~2.4", ">=2.4.0 <2.5.0-0"],
1710        tilde_with_greater_than_patch => ["~>3.2.1", ">=3.2.1 <3.3.0-0"],
1711        tilde_major_minor_zero => ["~1.1.0", ">=1.1.0 <1.2.0-0"],
1712        grater_than_equals_one => [">=1", ">=1.0.0"],
1713        greater_than_one => [">1", ">=2.0.0"],
1714        less_than_one_dot_two => ["<1.2", "<1.2.0-0"],
1715        greater_than_one_dot_two => [">1.2", ">=1.3.0"],
1716        greater_than_with_prerelease => [">1.1.0-beta-10", ">1.1.0-beta-10"],
1717        either_one_version_or_the_other => ["0.1.20 || 1.2.4", "0.1.20||1.2.4"],
1718        either_one_version_range_or_another => [">=0.2.3 || <0.0.1", ">=0.2.3||<0.0.1"],
1719        either_x_version_works => ["1.2.x || 2.x", ">=1.2.0 <1.3.0-0||>=2.0.0 <3.0.0-0"],
1720        either_asterisk_version_works => ["1.2.* || 2.*", ">=1.2.0 <1.3.0-0||>=2.0.0 <3.0.0-0"],
1721        one_two_three_or_greater_than_four => ["1.2.3 || >4", "1.2.3||>=5.0.0"],
1722        any_version_asterisk => ["*", ">=0.0.0"],
1723        any_version_x => ["x", ">=0.0.0"],
1724        whitespace_1 => [">= 1.0.0", ">=1.0.0"],
1725        whitespace_2 => [">=  1.0.0", ">=1.0.0"],
1726        whitespace_3 => [">=   1.0.0", ">=1.0.0"],
1727        whitespace_4 => ["> 1.0.0", ">1.0.0"],
1728        whitespace_5 => [">  1.0.0", ">1.0.0"],
1729        whitespace_6 => ["<=   2.0.0", "<=2.0.0"],
1730        whitespace_7 => ["<= 2.0.0", "<=2.0.0"],
1731        whitespace_8 => ["<=  2.0.0", "<=2.0.0"],
1732        whitespace_9 => ["<    2.0.0", "<2.0.0"],
1733        whitespace_10 => ["<\t2.0.0", "<2.0.0"],
1734        whitespace_11 => ["^ 1", ">=1.0.0 <2.0.0-0"],
1735        whitespace_12 => ["~> 1", ">=1.0.0 <2.0.0-0"],
1736        whitespace_13 => ["~ 1.0", ">=1.0.0 <1.1.0-0"],
1737        beta          => ["^0.0.1-beta", ">=0.0.1-beta <0.0.2-0"],
1738        beta_tilde => ["~1.2.3-beta", ">=1.2.3-beta <1.3.0-0"],
1739        beta_4        => ["^1.2.3-beta.4", ">=1.2.3-beta.4 <2.0.0-0"],
1740        pre_release_on_both => ["1.0.0-alpha - 2.0.0-beta", ">=1.0.0-alpha <=2.0.0-beta"],
1741        single_sided_lower_bound_with_pre_release => [">1.0.0-alpha", ">1.0.0-alpha"],
1742        space_separated1 => [">=1.2.3 <4.5.6", ">=1.2.3 <4.5.6"],
1743        garbage1 => ["1.2.3 foo", "1.2.3"],
1744        garbage2 => ["foo 1.2.3", "1.2.3"],
1745        garbage3 => ["~1.y 1.2.3", "1.2.3"],
1746        garbage4 => ["1.2.3 ~1.y", "1.2.3"],
1747        loose1 => [">01.02.03", ">1.2.3"],
1748        loose2 => ["~1.2.3beta", ">=1.2.3-beta <1.3.0-0"],
1749        caret_weird => ["^ 1.2 ^ 1", ">=1.2.0 <2.0.0-0"],
1750        loose_eq1 => ["=0.7", ">=0.7.0 <0.8.0-0"],
1751        loose_eq2 => ["=1", ">=1.0.0 <2.0.0-0"],
1752        consistent => ["^1.0.1", ">=1.0.1 <2.0.0-0"],
1753        consistent2 => [">=1.0.1 <2.0.0-0", ">=1.0.1 <2.0.0-0"],
1754    ];
1755
1756    /*
1757    // And these weirdos that I don't know what to do with.
1758    [">X", "<0.0.0-0"],
1759    ["<X", "<0.0.0-0"],
1760    ["<x <* || >* 2.x", "<0.0.0-0"],
1761    */
1762}
1763
1764#[cfg(test)]
1765mod ranges {
1766    use super::*;
1767
1768    #[test]
1769    fn one() {
1770        let r = BoundSet::new(
1771            Bound::Lower(Predicate::Including((1, 2, 0).into())),
1772            Bound::Upper(Predicate::Excluding((3, 3, 4).into())),
1773        )
1774        .unwrap();
1775
1776        assert_eq!(r.to_string(), ">=1.2.0 <3.3.4")
1777    }
1778}
1779
1780#[cfg(test)]
1781mod max_satisfying {
1782    use super::*;
1783
1784    fn assert_max_satisfying(versions: Vec<&str>, range: &str, expected: &str) {
1785        let versions: Vec<_> = versions
1786            .into_iter()
1787            .map(|s| Version::parse(s).unwrap())
1788            .collect();
1789        let range = Range::parse(range).unwrap();
1790        let result = range.max_satisfying(&versions);
1791
1792        assert_eq!(
1793            result,
1794            Some(&Version::parse(expected).unwrap()),
1795            "expected: {}, got: {:?}",
1796            expected,
1797            result
1798        );
1799    }
1800
1801    #[test]
1802    fn test_max_satisfying() {
1803        let cases = vec![
1804            (vec!["1.2.3", "1.2.4"], "1.2", "1.2.4"),
1805            (vec!["1.2.4", "1.2.3"], "1.2", "1.2.4"),
1806            (vec!["1.2.3", "1.2.4", "1.2.5", "1.2.6"], "~1.2.3", "1.2.6"),
1807            (
1808                vec!["1.1.0", "1.2.0", "1.2.1", "1.3.0", "2.0.0", "2.1.0"],
1809                "~2.0.0",
1810                "2.0.0",
1811            ),
1812        ];
1813
1814        for case in cases {
1815            assert_max_satisfying(case.0, case.1, case.2);
1816        }
1817    }
1818
1819    #[test]
1820    fn test_max_satisfying_empty() {
1821        let range = Range::parse("~1.2.3").unwrap();
1822        let versions = vec![];
1823        let result = range.max_satisfying(&versions);
1824
1825        assert_eq!(result, None);
1826    }
1827
1828    #[test]
1829    fn test_max_satisfying_none() {
1830        let range = Range::parse(">=1.0.0 <2.0.0").unwrap();
1831        let versions: Vec<_> = vec!["2.0.0", "0.1.0"]
1832            .iter()
1833            .map(|s| Version::parse(s).unwrap())
1834            .collect();
1835        let result = range.max_satisfying(&versions);
1836
1837        assert_eq!(result, None);
1838    }
1839}
1840
1841#[cfg(test)]
1842mod min_satisfying {
1843    use super::*;
1844
1845    fn assert_min_satisfying(versions: Vec<&str>, range: &str, expected: &str) {
1846        let versions: Vec<_> = versions
1847            .into_iter()
1848            .map(|s| Version::parse(s).unwrap())
1849            .collect();
1850        let range = Range::parse(range).unwrap();
1851        let result = range.min_satisfying(&versions);
1852
1853        assert_eq!(
1854            result,
1855            Some(&Version::parse(expected).unwrap()),
1856            "expected: {}, got: {:?}",
1857            expected,
1858            result
1859        );
1860    }
1861
1862    #[test]
1863    fn test_min_satisfying() {
1864        let cases = vec![
1865            (vec!["1.2.3", "1.2.4"], "1.2", "1.2.3"),
1866            (vec!["1.2.4", "1.2.3"], "1.2", "1.2.3"),
1867            (vec!["1.2.3", "1.2.4", "1.2.5", "1.2.6"], "~1.2.3", "1.2.3"),
1868            (
1869                vec!["1.1.0", "1.2.0", "1.2.1", "1.3.0", "2.0.0", "2.1.0"],
1870                "~2.0.0",
1871                "2.0.0",
1872            ),
1873        ];
1874
1875        for case in cases {
1876            assert_min_satisfying(case.0, case.1, case.2);
1877        }
1878    }
1879
1880    #[test]
1881    fn test_min_satisfying_empty() {
1882        let range = Range::parse("~1.2.3").unwrap();
1883        let versions = vec![];
1884        let result = range.min_satisfying(&versions);
1885
1886        assert_eq!(result, None);
1887    }
1888
1889    #[test]
1890    fn test_min_satisfying_none() {
1891        let range = Range::parse(">=1.0.0 <2.0.0").unwrap();
1892        let versions: Vec<_> = vec!["2.0.0", "0.1.0"]
1893            .iter()
1894            .map(|s| Version::parse(s).unwrap())
1895            .collect();
1896        let result = range.min_satisfying(&versions);
1897
1898        assert_eq!(result, None);
1899    }
1900}
1901
1902#[cfg(test)]
1903mod min_version {
1904    use super::*;
1905
1906    #[test]
1907    fn min_version_test() {
1908        // [range, minimum]
1909        let tests = vec![
1910            // Stars
1911            ("*", Some("0.0.0")),
1912            ("* || >=2", Some("0.0.0")),
1913            (">=2 || *", Some("0.0.0")),
1914            (">2 || *", Some("0.0.0")),
1915            // equal
1916            ("1.0.0", Some("1.0.0")),
1917            ("1.0", Some("1.0.0")),
1918            ("1.0.x", Some("1.0.0")),
1919            ("1.0.*", Some("1.0.0")),
1920            ("1", Some("1.0.0")),
1921            ("1.x.x", Some("1.0.0")),
1922            ("1.x.x", Some("1.0.0")),
1923            ("1.*.x", Some("1.0.0")),
1924            ("1.x.*", Some("1.0.0")),
1925            ("1.x", Some("1.0.0")),
1926            ("1.*", Some("1.0.0")),
1927            ("=1.0.0", Some("1.0.0")),
1928            // Tilde
1929            ("~1.1.1", Some("1.1.1")),
1930            ("~1.1.1-beta", Some("1.1.1-beta")),
1931            ("~1.1.1 || >=2", Some("1.1.1")),
1932            // Carot
1933            ("^1.1.1", Some("1.1.1")),
1934            ("^1.1.1-beta", Some("1.1.1-beta")),
1935            ("^1.1.1 || >=2", Some("1.1.1")),
1936            ("^2.16.2 ^2.16", Some("2.16.2")),
1937            // "-" operator
1938            ("1.1.1 - 1.8.0", Some("1.1.1")),
1939            ("1.1 - 1.8.0", Some("1.1.0")),
1940            // Less / less or equal
1941            ("<2", Some("0.0.0")),
1942            ("<0.0.0-beta", Some("0.0.0-0")),
1943            ("<0.0.1-beta", Some("0.0.0")),
1944            ("<2 || >4", Some("0.0.0")),
1945            (">4 || <2", Some("0.0.0")),
1946            ("<=2 || >=4", Some("0.0.0")),
1947            (">=4 || <=2", Some("0.0.0")),
1948            ("<0.0.0-beta >0.0.0-alpha", Some("0.0.0-alpha.0")),
1949            (">0.0.0-alpha <0.0.0-beta", Some("0.0.0-alpha.0")),
1950            // Greater than or equal
1951            (">=1.1.1 <2 || >=2.2.2 <3", Some("1.1.1")),
1952            (">=2.2.2 <3 || >=1.1.1 <2", Some("1.1.1")),
1953            // Greater than but not equal
1954            (">1.0.0", Some("1.0.1")),
1955            (">1.0.0-0", Some("1.0.0-0.0")),
1956            (">1.0.0-beta", Some("1.0.0-beta.0")),
1957            (">2 || >1.0.0", Some("1.0.1")),
1958            (">2 || >1.0.0-0", Some("1.0.0-0.0")),
1959            (">2 || >1.0.0-beta", Some("1.0.0-beta.0")),
1960        ];
1961
1962        for (range, version) in tests {
1963            let parsed_range = Range::parse(range).unwrap();
1964            let parsed_version = version.map(|v| Version::parse(v).unwrap());
1965            assert_eq!(
1966                parsed_range.min_version(),
1967                parsed_version,
1968                "expected min_version of {:?} to be {:?}",
1969                range,
1970                version
1971            );
1972        }
1973    }
1974}
1975
1976#[cfg(feature = "serde")]
1977#[cfg(test)]
1978mod serde_tests {
1979    use super::*;
1980
1981    #[test]
1982    fn test_serialize() {
1983        let range = Range::parse("~1.2.3").unwrap();
1984        let serialized = serde_json::to_string(&range).unwrap();
1985        let deserialized: Range = serde_json::from_str(&serialized).unwrap();
1986
1987        assert_eq!(range, deserialized);
1988    }
1989}