reproto_semver/
range.rs

1// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11use self::Op::{Compatible, Ex, Gt, GtEq, Lt, LtEq, Tilde, Wildcard};
12use self::WildcardVersion::{Minor, Patch};
13use errors::Error;
14use parser;
15#[cfg(feature = "serde")]
16use serde::de::{self, Deserialize, Deserializer, Visitor};
17#[cfg(feature = "serde")]
18use serde::ser::{Serialize, Serializer};
19use std::fmt;
20use std::str;
21use version::{Identifier, Version};
22
23/// A `Range` is a struct containing a list of predicates that can apply to ranges of version
24/// numbers. Matching operations can then be done with the `Range` against a particular
25/// version to see if it satisfies some or all of the constraints.
26#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
27pub struct Range {
28    pub predicates: Vec<Predicate>,
29}
30
31#[cfg(feature = "serde")]
32impl Serialize for Range {
33    fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
34    where
35        S: Serializer,
36    {
37        // Serialize Range as a string.
38        serializer.collect_str(self)
39    }
40}
41
42#[cfg(feature = "serde")]
43impl<'de> Deserialize<'de> for Range {
44    fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
45    where
46        D: Deserializer<'de>,
47    {
48        struct RangeVisitor;
49
50        /// Deserialize `Range` from a string.
51        impl<'de> Visitor<'de> for RangeVisitor {
52            type Value = Range;
53
54            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
55                formatter.write_str("a semver range without a string")
56            }
57
58            fn visit_str<E>(self, v: &str) -> ::std::result::Result<Self::Value, E>
59            where
60                E: de::Error,
61            {
62                Range::parse(v).map_err(de::Error::custom)
63            }
64        }
65
66        deserializer.deserialize_str(RangeVisitor)
67    }
68}
69
70#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
71pub enum WildcardVersion {
72    Minor,
73    Patch,
74}
75
76#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
77pub enum Op {
78    Ex,                        // Exact
79    Gt,                        // Greater than
80    GtEq,                      // Greater than or equal to
81    Lt,                        // Less than
82    LtEq,                      // Less than or equal to
83    Tilde,                     // e.g. ~1.0.0
84    Compatible,                // compatible by definition of semver, indicated by ^
85    Wildcard(WildcardVersion), // x.y.*, x.*, *
86}
87
88#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
89pub struct Predicate {
90    pub op: Op,
91    pub major: u64,
92    pub minor: Option<u64>,
93    pub patch: Option<u64>,
94    pub pre: Vec<Identifier>,
95}
96
97impl Range {
98    /// `any()` is a factory method which creates a `Range` with no constraints. In other
99    /// words, any version will match against it.
100    ///
101    /// # Examples
102    ///
103    /// ```
104    /// use reproto_semver::Range;
105    ///
106    /// let anything = Range::any();
107    /// ```
108    pub fn any() -> Range {
109        Range { predicates: vec![] }
110    }
111
112    /// `parse()` is the main constructor of a `Range`. It takes a string like `"^1.2.3"`
113    /// and turns it into a `Range` that matches that particular constraint.
114    ///
115    /// A `Result` is returned which contains a `Error` if there was a problem parsing the `Range`.
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// use reproto_semver::Range;
121    ///
122    /// let version = Range::parse("=1.2.3");
123    /// let version = Range::parse(">1.2.3");
124    /// let version = Range::parse("<1.2.3");
125    /// let version = Range::parse("~1.2.3");
126    /// let version = Range::parse("^1.2.3");
127    /// let version = Range::parse("1.2.3"); // synonym for ^1.2.3
128    /// let version = Range::parse("<=1.2.3");
129    /// let version = Range::parse(">=1.2.3");
130    /// ```
131    ///
132    /// This example demonstrates error handling, and will panic.
133    ///
134    /// ```should-panic
135    /// use reproto_semver::Range;
136    ///
137    /// let version = match Range::parse("not a version") {
138    ///     Ok(version) => version,
139    ///     Err(e) => panic!("There was a problem parsing: {}", e),
140    /// }
141    /// ```
142    pub fn parse(input: &str) -> Result<Range, Error> {
143        let mut parser = parser::Parser::new(input)?;
144        let range = parser.range()?;
145
146        if !parser.is_eof() {
147            return Err(Error::MoreInput);
148        }
149
150        Ok(range)
151    }
152
153    /// `exact()` is a factory method which creates a `Range` with one exact constraint.
154    ///
155    /// # Examples
156    ///
157    /// ```
158    /// use reproto_semver::Range;
159    /// use reproto_semver::Version;
160    ///
161    /// let version = Version { major: 1, minor: 1, patch: 1, pre: vec![], build: vec![] };
162    /// let exact = Range::exact(&version);
163    /// ```
164    pub fn exact(version: &Version) -> Range {
165        Range {
166            predicates: vec![Predicate::exact(version)],
167        }
168    }
169
170    /// `matches()` matches a given `Version` against this `Range`.
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// use reproto_semver::Range;
176    /// use reproto_semver::Version;
177    ///
178    /// let version = Version { major: 1, minor: 1, patch: 1, pre: vec![], build: vec![] };
179    /// let exact = Range::exact(&version);
180    ///
181    /// assert!(exact.matches(&version));
182    /// ```
183    pub fn matches(&self, version: &Version) -> bool {
184        // no predicates means anything matches
185        if self.predicates.is_empty() {
186            return true;
187        }
188
189        self.predicates.iter().all(|p| p.matches(version))
190            && self.predicates
191                .iter()
192                .any(|p| p.pre_tag_is_compatible(version))
193    }
194
195    /// Check if range matches any.
196    pub fn matches_any(&self) -> bool {
197        self.predicates.is_empty()
198    }
199}
200
201impl Predicate {
202    fn exact(version: &Version) -> Predicate {
203        Predicate {
204            op: Ex,
205            major: version.major,
206            minor: Some(version.minor),
207            patch: Some(version.patch),
208            pre: version.pre.clone(),
209        }
210    }
211
212    /// `matches()` takes a `Version` and determines if it matches this particular `Predicate`.
213    pub fn matches(&self, ver: &Version) -> bool {
214        match self.op {
215            Ex => self.is_exact(ver),
216            Gt => self.is_greater(ver),
217            GtEq => self.is_exact(ver) || self.is_greater(ver),
218            Lt => !self.is_exact(ver) && !self.is_greater(ver),
219            LtEq => !self.is_greater(ver),
220            Tilde => self.matches_tilde(ver),
221            Compatible => self.is_compatible(ver),
222            Wildcard(_) => self.matches_wildcard(ver),
223        }
224    }
225
226    fn is_exact(&self, ver: &Version) -> bool {
227        if self.major != ver.major {
228            return false;
229        }
230
231        match self.minor {
232            Some(minor) => {
233                if minor != ver.minor {
234                    return false;
235                }
236            }
237            None => return true,
238        }
239
240        match self.patch {
241            Some(patch) => {
242                if patch != ver.patch {
243                    return false;
244                }
245            }
246            None => return true,
247        }
248
249        if self.pre != ver.pre {
250            return false;
251        }
252
253        true
254    }
255
256    // https://docs.npmjs.com/misc/semver#prerelease-tags
257    fn pre_tag_is_compatible(&self, ver: &Version) -> bool {
258        // If a version has a prerelease tag (for example, 1.2.3-alpha.3) then it will
259        // only be
260        // allowed to satisfy comparator sets if at least one comparator with the same
261        // [major,
262        // minor, patch] tuple also has a prerelease tag.
263        !ver.is_prerelease()
264            || (self.major == ver.major && self.minor == Some(ver.minor)
265                && self.patch == Some(ver.patch) && !self.pre.is_empty())
266    }
267
268    fn is_greater(&self, ver: &Version) -> bool {
269        if self.major != ver.major {
270            return ver.major > self.major;
271        }
272
273        match self.minor {
274            Some(minor) => {
275                if minor != ver.minor {
276                    return ver.minor > minor;
277                }
278            }
279            None => return false,
280        }
281
282        match self.patch {
283            Some(patch) => {
284                if patch != ver.patch {
285                    return ver.patch > patch;
286                }
287            }
288            None => return false,
289        }
290
291        if !self.pre.is_empty() {
292            return ver.pre.is_empty() || ver.pre > self.pre;
293        }
294
295        false
296    }
297
298    // see https://www.npmjs.org/doc/misc/semver.html for behavior
299    fn matches_tilde(&self, ver: &Version) -> bool {
300        let minor = match self.minor {
301            Some(n) => n,
302            None => return self.major == ver.major,
303        };
304
305        match self.patch {
306            Some(patch) => {
307                self.major == ver.major && minor == ver.minor
308                    && (ver.patch > patch || (ver.patch == patch && self.pre_is_compatible(ver)))
309            }
310            None => self.major == ver.major && minor == ver.minor,
311        }
312    }
313
314    // see https://www.npmjs.org/doc/misc/semver.html for behavior
315    fn is_compatible(&self, ver: &Version) -> bool {
316        if self.major != ver.major {
317            return false;
318        }
319
320        let minor = match self.minor {
321            Some(n) => n,
322            None => return self.major == ver.major,
323        };
324
325        match self.patch {
326            Some(patch) => {
327                if self.major == 0 {
328                    if minor == 0 {
329                        ver.minor == minor && ver.patch == patch && self.pre_is_compatible(ver)
330                    } else {
331                        ver.minor == minor
332                            && (ver.patch > patch
333                                || (ver.patch == patch && self.pre_is_compatible(ver)))
334                    }
335                } else {
336                    ver.minor > minor
337                        || (ver.minor == minor
338                            && (ver.patch > patch
339                                || (ver.patch == patch && self.pre_is_compatible(ver))))
340                }
341            }
342            None => {
343                if self.major == 0 {
344                    ver.minor == minor
345                } else {
346                    ver.minor >= minor
347                }
348            }
349        }
350    }
351
352    fn pre_is_compatible(&self, ver: &Version) -> bool {
353        ver.pre.is_empty() || ver.pre >= self.pre
354    }
355
356    // see https://www.npmjs.org/doc/misc/semver.html for behavior
357    fn matches_wildcard(&self, ver: &Version) -> bool {
358        match self.op {
359            Wildcard(Minor) => self.major == ver.major,
360            Wildcard(Patch) => {
361                match self.minor {
362                    Some(minor) => self.major == ver.major && minor == ver.minor,
363                    None => {
364                        // minor and patch version astericks mean match on major
365                        self.major == ver.major
366                    }
367                }
368            }
369            _ => false, // unreachable
370        }
371    }
372}
373
374impl fmt::Display for Range {
375    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
376        if self.predicates.is_empty() {
377            try!(write!(fmt, "*"));
378        } else {
379            for (i, ref pred) in self.predicates.iter().enumerate() {
380                if i == 0 {
381                    try!(write!(fmt, "{}", pred));
382                } else {
383                    try!(write!(fmt, ", {}", pred));
384                }
385            }
386        }
387
388        Ok(())
389    }
390}
391
392impl fmt::Display for Predicate {
393    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
394        match self.op {
395            Wildcard(Minor) => try!(write!(fmt, "{}.*", self.major)),
396            Wildcard(Patch) => {
397                if let Some(minor) = self.minor {
398                    try!(write!(fmt, "{}.{}.*", self.major, minor))
399                } else {
400                    try!(write!(fmt, "{}.*.*", self.major))
401                }
402            }
403            _ => {
404                try!(write!(fmt, "{}{}", self.op, self.major));
405
406                match self.minor {
407                    Some(v) => try!(write!(fmt, ".{}", v)),
408                    None => (),
409                }
410
411                match self.patch {
412                    Some(v) => try!(write!(fmt, ".{}", v)),
413                    None => (),
414                }
415
416                if !self.pre.is_empty() {
417                    try!(write!(fmt, "-"));
418                    for (i, x) in self.pre.iter().enumerate() {
419                        if i != 0 {
420                            try!(write!(fmt, "."))
421                        }
422                        try!(write!(fmt, "{}", x));
423                    }
424                }
425            }
426        }
427
428        Ok(())
429    }
430}
431
432impl fmt::Display for Op {
433    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
434        match *self {
435            Ex => try!(write!(fmt, "= ")),
436            Gt => try!(write!(fmt, "> ")),
437            GtEq => try!(write!(fmt, ">= ")),
438            Lt => try!(write!(fmt, "< ")),
439            LtEq => try!(write!(fmt, "<= ")),
440            Tilde => try!(write!(fmt, "~")),
441            Compatible => try!(write!(fmt, "^")),
442            // gets handled specially in Predicate::fmt
443            Wildcard(_) => try!(write!(fmt, "")),
444        }
445        Ok(())
446    }
447}
448
449#[cfg(test)]
450mod test {
451    use super::super::version::Version;
452    use super::{Op, Range};
453    use std::hash::{Hash, Hasher};
454
455    fn range(s: &str) -> Range {
456        Range::parse(s).unwrap()
457    }
458
459    fn version(s: &str) -> Version {
460        match Version::parse(s) {
461            Ok(v) => v,
462            Err(e) => panic!("`{}` is not a valid version. Reason: {:?}", s, e),
463        }
464    }
465
466    fn assert_match(range: &Range, vers: &[&str]) {
467        for ver in vers.iter() {
468            assert!(range.matches(&version(*ver)), "did not match {}", ver);
469        }
470    }
471
472    fn assert_not_match(range: &Range, vers: &[&str]) {
473        for ver in vers.iter() {
474            assert!(!range.matches(&version(*ver)), "matched {}", ver);
475        }
476    }
477
478    fn calculate_hash<T: Hash>(t: T) -> u64 {
479        use std::collections::hash_map::DefaultHasher;
480
481        let mut s = DefaultHasher::new();
482        t.hash(&mut s);
483        s.finish()
484    }
485
486    #[test]
487    fn test_parsing_default() {
488        let r = range("1.0.0");
489
490        assert_eq!(r.to_string(), "^1.0.0".to_string());
491
492        assert_match(&r, &["1.0.0", "1.0.1"]);
493        assert_not_match(&r, &["0.9.9", "0.10.0", "0.1.0"]);
494    }
495
496    #[test]
497    fn test_parsing_exact() {
498        let r = range("=1.0.0");
499
500        assert!(r.to_string() == "= 1.0.0".to_string());
501        assert_eq!(r.to_string(), "= 1.0.0".to_string());
502
503        assert_match(&r, &["1.0.0"]);
504        assert_not_match(&r, &["1.0.1", "0.9.9", "0.10.0", "0.1.0", "1.0.0-pre"]);
505
506        let r = range("=0.9.0");
507
508        assert_eq!(r.to_string(), "= 0.9.0".to_string());
509
510        assert_match(&r, &["0.9.0"]);
511        assert_not_match(&r, &["0.9.1", "1.9.0", "0.0.9"]);
512
513        let r = range("=0.1.0-beta2.a");
514
515        assert_eq!(r.to_string(), "= 0.1.0-beta2.a".to_string());
516
517        assert_match(&r, &["0.1.0-beta2.a"]);
518        assert_not_match(&r, &["0.9.1", "0.1.0", "0.1.1-beta2.a", "0.1.0-beta2"]);
519    }
520
521    #[test]
522    fn test_parse_metadata_see_issue_88_see_issue_88() {
523        for op in &[
524            Op::Compatible,
525            Op::Ex,
526            Op::Gt,
527            Op::GtEq,
528            Op::Lt,
529            Op::LtEq,
530            Op::Tilde,
531        ] {
532            range(&format!("{} 1.2.3+meta", op));
533        }
534    }
535
536    #[test]
537    pub fn test_parsing_greater_than() {
538        let r = range(">= 1.0.0");
539
540        assert_eq!(r.to_string(), ">= 1.0.0".to_string());
541
542        assert_match(&r, &["1.0.0", "2.0.0"]);
543        assert_not_match(&r, &["0.1.0", "0.0.1", "1.0.0-pre", "2.0.0-pre"]);
544
545        let r = range(">= 2.1.0-alpha2");
546
547        assert_match(&r, &["2.1.0-alpha2", "2.1.0-alpha3", "2.1.0", "3.0.0"]);
548        assert_not_match(
549            &r,
550            &["2.0.0", "2.1.0-alpha1", "2.0.0-alpha2", "3.0.0-alpha2"],
551        );
552    }
553
554    #[test]
555    pub fn test_parsing_less_than() {
556        let r = range("< 1.0.0");
557
558        assert_eq!(r.to_string(), "< 1.0.0".to_string());
559
560        assert_match(&r, &["0.1.0", "0.0.1"]);
561        assert_not_match(&r, &["1.0.0", "1.0.0-beta", "1.0.1", "0.9.9-alpha"]);
562
563        let r = range("<= 2.1.0-alpha2");
564
565        assert_match(&r, &["2.1.0-alpha2", "2.1.0-alpha1", "2.0.0", "1.0.0"]);
566        assert_not_match(
567            &r,
568            &["2.1.0", "2.2.0-alpha1", "2.0.0-alpha2", "1.0.0-alpha2"],
569        );
570    }
571
572    #[test]
573    pub fn test_multiple() {
574        let r = range("> 0.0.9, <= 2.5.3");
575        assert_eq!(r.to_string(), "> 0.0.9, <= 2.5.3".to_string());
576        assert_match(&r, &["0.0.10", "1.0.0", "2.5.3"]);
577        assert_not_match(&r, &["0.0.8", "2.5.4"]);
578
579        let r = range("0.3.0, 0.4.0");
580        assert_eq!(r.to_string(), "^0.3.0, ^0.4.0".to_string());
581        assert_not_match(&r, &["0.0.8", "0.3.0", "0.4.0"]);
582
583        let r = range("<= 0.2.0, >= 0.5.0");
584        assert_eq!(r.to_string(), "<= 0.2.0, >= 0.5.0".to_string());
585        assert_not_match(&r, &["0.0.8", "0.3.0", "0.5.1"]);
586
587        let r = range("0.1.0, 0.1.4, 0.1.6");
588        assert_eq!(r.to_string(), "^0.1.0, ^0.1.4, ^0.1.6".to_string());
589        assert_match(&r, &["0.1.6", "0.1.9"]);
590        assert_not_match(&r, &["0.1.0", "0.1.4", "0.2.0"]);
591
592        assert!(Range::parse("> 0.1.0,").is_err());
593        assert!(Range::parse("> 0.3.0, ,").is_err());
594
595        let r = range(">=0.5.1-alpha3, <0.6");
596        assert_eq!(r.to_string(), ">= 0.5.1-alpha3, < 0.6".to_string());
597        assert_match(
598            &r,
599            &[
600                "0.5.1-alpha3",
601                "0.5.1-alpha4",
602                "0.5.1-beta",
603                "0.5.1",
604                "0.5.5",
605            ],
606        );
607        assert_not_match(
608            &r,
609            &["0.5.1-alpha1", "0.5.2-alpha3", "0.5.5-pre", "0.5.0-pre"],
610        );
611        assert_not_match(&r, &["0.6.0", "0.6.0-pre"]);
612    }
613
614    #[test]
615    pub fn test_parsing_tilde() {
616        let r = range("~1");
617        assert_match(&r, &["1.0.0", "1.0.1", "1.1.1"]);
618        assert_not_match(&r, &["0.9.1", "2.9.0", "0.0.9"]);
619
620        let r = range("~1.2");
621        assert_match(&r, &["1.2.0", "1.2.1"]);
622        assert_not_match(&r, &["1.1.1", "1.3.0", "0.0.9"]);
623
624        let r = range("~1.2.2");
625        assert_match(&r, &["1.2.2", "1.2.4"]);
626        assert_not_match(&r, &["1.2.1", "1.9.0", "1.0.9", "2.0.1", "0.1.3"]);
627
628        let r = range("~1.2.3-beta.2");
629        assert_match(&r, &["1.2.3", "1.2.4", "1.2.3-beta.2", "1.2.3-beta.4"]);
630        assert_not_match(&r, &["1.3.3", "1.1.4", "1.2.3-beta.1", "1.2.4-beta.2"]);
631    }
632
633    #[test]
634    pub fn test_parsing_compatible() {
635        let r = range("^1");
636        assert_match(&r, &["1.1.2", "1.1.0", "1.2.1", "1.0.1"]);
637        assert_not_match(&r, &["0.9.1", "2.9.0", "0.1.4"]);
638        assert_not_match(&r, &["1.0.0-beta1", "0.1.0-alpha", "1.0.1-pre"]);
639
640        let r = range("^1.1");
641        assert_match(&r, &["1.1.2", "1.1.0", "1.2.1"]);
642        assert_not_match(&r, &["0.9.1", "2.9.0", "1.0.1", "0.1.4"]);
643
644        let r = range("^1.1.2");
645        assert_match(&r, &["1.1.2", "1.1.4", "1.2.1"]);
646        assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1"]);
647        assert_not_match(&r, &["1.1.2-alpha1", "1.1.3-alpha1", "2.9.0-alpha1"]);
648
649        let r = range("^0.1.2");
650        assert_match(&r, &["0.1.2", "0.1.4"]);
651        assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1"]);
652        assert_not_match(&r, &["0.1.2-beta", "0.1.3-alpha", "0.2.0-pre"]);
653
654        let r = range("^0.5.1-alpha3");
655        assert_match(
656            &r,
657            &[
658                "0.5.1-alpha3",
659                "0.5.1-alpha4",
660                "0.5.1-beta",
661                "0.5.1",
662                "0.5.5",
663            ],
664        );
665        assert_not_match(
666            &r,
667            &[
668                "0.5.1-alpha1",
669                "0.5.2-alpha3",
670                "0.5.5-pre",
671                "0.5.0-pre",
672                "0.6.0",
673            ],
674        );
675
676        let r = range("^0.0.2");
677        assert_match(&r, &["0.0.2"]);
678        assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1", "0.1.4"]);
679
680        let r = range("^0.0");
681        assert_match(&r, &["0.0.2", "0.0.0"]);
682        assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.1.4"]);
683
684        let r = range("^0");
685        assert_match(&r, &["0.9.1", "0.0.2", "0.0.0"]);
686        assert_not_match(&r, &["2.9.0", "1.1.1"]);
687
688        let r = range("^1.4.2-beta.5");
689        assert_match(
690            &r,
691            &["1.4.2", "1.4.3", "1.4.2-beta.5", "1.4.2-beta.6", "1.4.2-c"],
692        );
693        assert_not_match(
694            &r,
695            &[
696                "0.9.9",
697                "2.0.0",
698                "1.4.2-alpha",
699                "1.4.2-beta.4",
700                "1.4.3-beta.5",
701            ],
702        );
703    }
704
705    #[test]
706    pub fn test_parsing_wildcard() {
707        let r = range("");
708        assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);
709        assert_not_match(&r, &[]);
710        let r = range("*");
711        assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);
712        assert_not_match(&r, &[]);
713        let r = range("x");
714        assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);
715        assert_not_match(&r, &[]);
716        let r = range("X");
717        assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);
718        assert_not_match(&r, &[]);
719
720        let r = range("1.*");
721        assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]);
722        assert_not_match(&r, &["0.0.9"]);
723        let r = range("1.x");
724        assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]);
725        assert_not_match(&r, &["0.0.9"]);
726        let r = range("1.X");
727        assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]);
728        assert_not_match(&r, &["0.0.9"]);
729
730        let r = range("1.2.*");
731        assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]);
732        assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]);
733        let r = range("1.2.x");
734        assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]);
735        assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]);
736        let r = range("1.2.X");
737        assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]);
738        assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]);
739    }
740
741    #[test]
742    pub fn test_any() {
743        let r = Range::any();
744        assert_match(&r, &["0.0.1", "0.1.0", "1.0.0"]);
745    }
746
747    #[test]
748    pub fn test_pre() {
749        let r = range("=2.1.1-really.0");
750        assert_match(&r, &["2.1.1-really.0"]);
751    }
752
753    #[test]
754    pub fn test_from_str() {
755        assert_eq!(
756            Range::parse("1.0.0").unwrap().to_string(),
757            "^1.0.0".to_string()
758        );
759        assert_eq!(
760            Range::parse("=1.0.0").unwrap().to_string(),
761            "= 1.0.0".to_string()
762        );
763        assert_eq!(Range::parse("~1").unwrap().to_string(), "~1".to_string());
764        assert_eq!(
765            Range::parse("~1.2").unwrap().to_string(),
766            "~1.2".to_string()
767        );
768        assert_eq!(Range::parse("^1").unwrap().to_string(), "^1".to_string());
769        assert_eq!(
770            Range::parse("^1.1").unwrap().to_string(),
771            "^1.1".to_string()
772        );
773        assert_eq!(Range::parse("*").unwrap().to_string(), "*".to_string());
774        assert_eq!(Range::parse("1.*").unwrap().to_string(), "1.*".to_string());
775        assert_eq!(
776            Range::parse("< 1.0.0").unwrap().to_string(),
777            "< 1.0.0".to_string()
778        );
779    }
780
781    #[test]
782    fn test_cargo3202() {
783        let v = Range::parse("0.*.*").unwrap();
784        assert_eq!("0.*.*", format!("{}", v.predicates[0]));
785
786        let v = Range::parse("0.0.*").unwrap();
787        assert_eq!("0.0.*", format!("{}", v.predicates[0]));
788
789        let r = range("0.*.*");
790        assert_match(&r, &["0.5.0"]);
791    }
792
793    #[test]
794    fn test_eq_hash() {
795        assert!(range("^1") == range("^1"));
796        assert!(calculate_hash(range("^1")) == calculate_hash(range("^1")));
797        assert!(range("^1") != range("^2"));
798    }
799
800    #[test]
801    fn test_ordering() {
802        assert!(range("=1") > range("*"));
803        assert!(range(">1") > range("*"));
804        assert!(range(">=1") > range("*"));
805        assert!(range("<1") > range("*"));
806        assert!(range("<=1") > range("*"));
807        assert!(range("~1") > range("*"));
808        assert!(range("^1") > range("*"));
809        assert!(range("*") == range("*"));
810    }
811}