node_semver/
range.rs

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