--- /dev/null
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT\r
+// file at the top-level directory of this distribution and at\r
+// http://rust-lang.org/COPYRIGHT.\r
+//\r
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or\r
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license\r
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your\r
+// option. This file may not be copied, modified, or distributed\r
+// except according to those terms.\r
+\r
+use std::error::Error;\r
+use std::fmt;\r
+use std::str;\r
+\r
+use semver_parser;\r
+use semver_parser::{Compat, RangeSet};\r
+use version::Identifier;\r
+use Version;\r
+\r
+#[cfg(feature = "serde")]\r
+use serde::de::{self, Deserialize, Deserializer, Visitor};\r
+#[cfg(feature = "serde")]\r
+use serde::ser::{Serialize, Serializer};\r
+\r
+use self::Op::{Ex, Gt, GtEq, Lt, LtEq};\r
+use self::ReqParseError::*;\r
+\r
+/// A `VersionReq` is a struct containing a list of ranges that can apply to ranges of version\r
+/// numbers. Matching operations can then be done with the `VersionReq` against a particular\r
+/// version to see if it satisfies some or all of the constraints.\r
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]\r
+#[cfg_attr(feature = "diesel", derive(AsExpression, FromSqlRow))]\r
+#[cfg_attr(feature = "diesel", sql_type = "diesel::sql_types::Text")]\r
+pub struct VersionReq {\r
+ ranges: Vec<Range>,\r
+ compat: Compat, // defaults to Cargo\r
+}\r
+\r
+impl From<semver_parser::RangeSet> for VersionReq {\r
+ fn from(range_set: semver_parser::RangeSet) -> VersionReq {\r
+ VersionReq {\r
+ ranges: range_set.ranges.into_iter().map(From::from).collect(),\r
+ compat: range_set.compat,\r
+ }\r
+ }\r
+}\r
+\r
+#[cfg(feature = "serde")]\r
+impl Serialize for VersionReq {\r
+ fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>\r
+ where\r
+ S: Serializer,\r
+ {\r
+ // Serialize VersionReq as a string.\r
+ serializer.collect_str(self)\r
+ }\r
+}\r
+\r
+// TODO: how to implement deserialize with compatibility?\r
+#[cfg(feature = "serde")]\r
+impl<'de> Deserialize<'de> for VersionReq {\r
+ fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>\r
+ where\r
+ D: Deserializer<'de>,\r
+ {\r
+ struct VersionReqVisitor;\r
+\r
+ /// Deserialize `VersionReq` from a string.\r
+ impl<'de> Visitor<'de> for VersionReqVisitor {\r
+ type Value = VersionReq;\r
+\r
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\r
+ formatter.write_str("a SemVer version requirement as a string")\r
+ }\r
+\r
+ fn visit_str<E>(self, v: &str) -> ::std::result::Result<Self::Value, E>\r
+ where\r
+ E: de::Error,\r
+ {\r
+ VersionReq::parse(v).map_err(de::Error::custom)\r
+ }\r
+ }\r
+\r
+ deserializer.deserialize_str(VersionReqVisitor)\r
+ }\r
+}\r
+\r
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]\r
+enum Op {\r
+ Ex, // Exact\r
+ Gt, // Greater than\r
+ GtEq, // Greater than or equal to\r
+ Lt, // Less than\r
+ LtEq, // Less than or equal to\r
+}\r
+\r
+impl From<semver_parser::Op> for Op {\r
+ fn from(op: semver_parser::Op) -> Op {\r
+ match op {\r
+ semver_parser::Op::Eq => Op::Ex,\r
+ semver_parser::Op::Gt => Op::Gt,\r
+ semver_parser::Op::Gte => Op::GtEq,\r
+ semver_parser::Op::Lt => Op::Lt,\r
+ semver_parser::Op::Lte => Op::LtEq,\r
+ }\r
+ }\r
+}\r
+\r
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]\r
+struct Range {\r
+ predicates: Vec<Predicate>,\r
+ compat: Compat,\r
+}\r
+\r
+impl From<semver_parser::Range> for Range {\r
+ fn from(range: semver_parser::Range) -> Range {\r
+ Range {\r
+ predicates: range.comparator_set.into_iter().map(From::from).collect(),\r
+ compat: range.compat,\r
+ }\r
+ }\r
+}\r
+\r
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]\r
+struct Predicate {\r
+ op: Op,\r
+ major: u64,\r
+ minor: u64,\r
+ patch: u64,\r
+ pre: Vec<Identifier>,\r
+}\r
+\r
+impl From<semver_parser::Comparator> for Predicate {\r
+ fn from(comparator: semver_parser::Comparator) -> Predicate {\r
+ Predicate {\r
+ op: From::from(comparator.op),\r
+ major: comparator.major,\r
+ minor: comparator.minor,\r
+ patch: comparator.patch,\r
+ pre: comparator.pre.into_iter().map(From::from).collect(),\r
+ }\r
+ }\r
+}\r
+\r
+impl From<semver_parser::Identifier> for Identifier {\r
+ fn from(identifier: semver_parser::Identifier) -> Identifier {\r
+ match identifier {\r
+ semver_parser::Identifier::Numeric(n) => Identifier::Numeric(n),\r
+ semver_parser::Identifier::AlphaNumeric(s) => Identifier::AlphaNumeric(s),\r
+ }\r
+ }\r
+}\r
+\r
+/// A `ReqParseError` is returned from methods which parse a string into a [`VersionReq`]. Each\r
+/// enumeration is one of the possible errors that can occur.\r
+/// [`VersionReq`]: struct.VersionReq.html\r
+#[derive(Clone, Debug, PartialEq)]\r
+pub enum ReqParseError {\r
+ /// The given version requirement is invalid.\r
+ InvalidVersionRequirement,\r
+ /// You have already provided an operation, such as `=`, `~`, or `^`. Only use one.\r
+ OpAlreadySet,\r
+ /// The sigil you have written is not correct.\r
+ InvalidSigil,\r
+ /// All components of a version must be numeric.\r
+ VersionComponentsMustBeNumeric,\r
+ /// There was an error parsing an identifier.\r
+ InvalidIdentifier,\r
+ /// At least a major version is required.\r
+ MajorVersionRequired,\r
+ /// An unimplemented version requirement.\r
+ UnimplementedVersionRequirement,\r
+ /// This form of requirement is deprecated.\r
+ DeprecatedVersionRequirement(VersionReq),\r
+}\r
+\r
+impl fmt::Display for ReqParseError {\r
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\r
+ let msg = match self {\r
+ InvalidVersionRequirement => "the given version requirement is invalid",\r
+ OpAlreadySet => {\r
+ "you have already provided an operation, such as =, ~, or ^; only use one"\r
+ }\r
+ InvalidSigil => "the sigil you have written is not correct",\r
+ VersionComponentsMustBeNumeric => "version components must be numeric",\r
+ InvalidIdentifier => "invalid identifier",\r
+ MajorVersionRequired => "at least a major version number is required",\r
+ UnimplementedVersionRequirement => {\r
+ "the given version requirement is not implemented, yet"\r
+ }\r
+ DeprecatedVersionRequirement(_) => "This requirement is deprecated",\r
+ };\r
+ msg.fmt(f)\r
+ }\r
+}\r
+\r
+impl Error for ReqParseError {}\r
+\r
+impl From<String> for ReqParseError {\r
+ fn from(other: String) -> ReqParseError {\r
+ match &*other {\r
+ "Null is not a valid VersionReq" => ReqParseError::InvalidVersionRequirement,\r
+ "VersionReq did not parse properly." => ReqParseError::OpAlreadySet,\r
+ _ => ReqParseError::InvalidVersionRequirement,\r
+ }\r
+ }\r
+}\r
+\r
+impl VersionReq {\r
+ /// `any()` is a factory method which creates a `VersionReq` with no constraints. In other\r
+ /// words, any version will match against it.\r
+ ///\r
+ /// # Examples\r
+ ///\r
+ /// ```\r
+ /// use semver::VersionReq;\r
+ ///\r
+ /// let anything = VersionReq::any();\r
+ /// ```\r
+ pub fn any() -> VersionReq {\r
+ VersionReq {\r
+ ranges: vec![],\r
+ compat: Compat::Cargo,\r
+ }\r
+ }\r
+\r
+ /// `parse()` is the main constructor of a `VersionReq`. It takes a string like `"^1.2.3"`\r
+ /// and turns it into a `VersionReq` that matches that particular constraint.\r
+ ///\r
+ /// A `Result` is returned which contains a [`ReqParseError`] if there was a problem parsing the\r
+ /// `VersionReq`.\r
+ /// [`ReqParseError`]: enum.ReqParseError.html\r
+ ///\r
+ /// # Examples\r
+ ///\r
+ /// ```\r
+ /// use semver::VersionReq;\r
+ ///\r
+ /// let version = VersionReq::parse("=1.2.3");\r
+ /// let version = VersionReq::parse(">1.2.3");\r
+ /// let version = VersionReq::parse("<1.2.3");\r
+ /// let version = VersionReq::parse("~1.2.3");\r
+ /// let version = VersionReq::parse("^1.2.3");\r
+ /// let version = VersionReq::parse("1.2.3"); // synonym for ^1.2.3\r
+ /// let version = VersionReq::parse("<=1.2.3");\r
+ /// let version = VersionReq::parse(">=1.2.3");\r
+ /// ```\r
+ ///\r
+ /// This example demonstrates error handling, and will panic.\r
+ ///\r
+ /// ```should_panic\r
+ /// use semver::VersionReq;\r
+ ///\r
+ /// let version = match VersionReq::parse("not a version") {\r
+ /// Ok(version) => version,\r
+ /// Err(e) => panic!("There was a problem parsing: {}", e),\r
+ /// };\r
+ /// ```\r
+ ///\r
+ /// # Errors\r
+ ///\r
+ /// Returns an error variant if the input could not be parsed as a semver requirement.\r
+ ///\r
+ /// Examples of common error causes are as follows:\r
+ ///\r
+ /// * `\0` - an invalid version requirement is used.\r
+ /// * `>= >= 1.2.3` - multiple operations are used. Only use one.\r
+ /// * `>== 1.2.3` - an invalid operation is used.\r
+ /// * `a.0.0` - version components are not numeric.\r
+ /// * `1.2.3-` - an invalid identifier is present.\r
+ /// * `>=` - major version was not specified. At least a major version is required.\r
+ /// * `0.2*` - deprecated requirement syntax. Equivalent would be `0.2.*`.\r
+ ///\r
+ /// You may also encounter an `UnimplementedVersionRequirement` error, which indicates that a\r
+ /// given requirement syntax is not yet implemented in this crate.\r
+ pub fn parse(input: &str) -> Result<VersionReq, ReqParseError> {\r
+ let range_set = input.parse::<RangeSet>();\r
+\r
+ if let Ok(v) = range_set {\r
+ return Ok(From::from(v));\r
+ }\r
+\r
+ match VersionReq::parse_deprecated(input) {\r
+ Some(v) => Err(ReqParseError::DeprecatedVersionRequirement(v)),\r
+ None => Err(From::from(range_set.err().unwrap())),\r
+ }\r
+ }\r
+\r
+ // TODO: better docs for this\r
+ /// `parse_compat()` is like `parse()`, but it takes an extra argument for compatibility with\r
+ /// other semver implementations, and turns that into a `VersionReq` that matches the\r
+ /// particular constraint and compatibility.\r
+ ///\r
+ /// A `Result` is returned which contains a [`ReqParseError`] if there was a problem parsing the\r
+ /// `VersionReq`.\r
+ /// [`ReqParseError`]: enum.ReqParseError.html\r
+ ///\r
+ /// # Examples\r
+ ///\r
+ /// ```\r
+ /// extern crate semver_parser;\r
+ /// use semver::VersionReq;\r
+ /// use semver_parser::Compat;\r
+ ///\r
+ /// # fn main() {\r
+ /// let cargo_version = VersionReq::parse_compat("1.2.3", Compat::Cargo);\r
+ /// let npm_version = VersionReq::parse_compat("1.2.3", Compat::Npm);\r
+ /// # }\r
+ /// ```\r
+ pub fn parse_compat(input: &str, compat: Compat) -> Result<VersionReq, ReqParseError> {\r
+ let range_set = RangeSet::parse(input, compat);\r
+\r
+ if let Ok(v) = range_set {\r
+ return Ok(From::from(v));\r
+ }\r
+\r
+ match VersionReq::parse_deprecated(input) {\r
+ Some(v) => Err(ReqParseError::DeprecatedVersionRequirement(v)),\r
+ None => Err(From::from(range_set.err().unwrap())),\r
+ }\r
+ }\r
+\r
+ fn parse_deprecated(version: &str) -> Option<VersionReq> {\r
+ match version {\r
+ ".*" => Some(VersionReq::any()),\r
+ "0.1.0." => Some(VersionReq::parse("0.1.0").unwrap()),\r
+ "0.3.1.3" => Some(VersionReq::parse("0.3.13").unwrap()),\r
+ "0.2*" => Some(VersionReq::parse("0.2.*").unwrap()),\r
+ "*.0" => Some(VersionReq::any()),\r
+ _ => None,\r
+ }\r
+ }\r
+\r
+ /// `exact()` is a factory method which creates a `VersionReq` with one exact constraint.\r
+ ///\r
+ /// # Examples\r
+ ///\r
+ /// ```\r
+ /// use semver::VersionReq;\r
+ /// use semver::Version;\r
+ ///\r
+ /// let version = Version { major: 1, minor: 1, patch: 1, pre: vec![], build: vec![] };\r
+ /// let exact = VersionReq::exact(&version);\r
+ /// ```\r
+ pub fn exact(version: &Version) -> VersionReq {\r
+ VersionReq {\r
+ ranges: vec![Range {\r
+ predicates: vec![Predicate::exact(version)],\r
+ compat: Compat::Cargo,\r
+ }],\r
+ compat: Compat::Cargo,\r
+ }\r
+ }\r
+\r
+ /// `matches()` matches a given [`Version`] against this `VersionReq`.\r
+ /// [`Version`]: struct.Version.html\r
+ ///\r
+ /// # Examples\r
+ ///\r
+ /// ```\r
+ /// use semver::VersionReq;\r
+ /// use semver::Version;\r
+ ///\r
+ /// let version = Version { major: 1, minor: 1, patch: 1, pre: vec![], build: vec![] };\r
+ /// let exact = VersionReq::exact(&version);\r
+ ///\r
+ /// assert!(exact.matches(&version));\r
+ /// ```\r
+ pub fn matches(&self, version: &Version) -> bool {\r
+ // no ranges means anything matches\r
+ if self.ranges.is_empty() {\r
+ return true;\r
+ }\r
+\r
+ self.ranges\r
+ .iter()\r
+ .any(|r| r.matches(version) && r.pre_tag_is_compatible(version))\r
+ }\r
+\r
+ /// `is_exact()` returns `true` if there is exactly one version which could match this\r
+ /// `VersionReq`. If `false` is returned, it is possible that there may still only be exactly\r
+ /// one version which could match this `VersionReq`. This function is intended do allow\r
+ /// short-circuiting more complex logic where being able to handle only the possibility of a\r
+ /// single exact version may be cheaper.\r
+ ///\r
+ /// # Examples\r
+ ///\r
+ /// ```\r
+ /// use semver::ReqParseError;\r
+ /// use semver::VersionReq;\r
+ ///\r
+ /// fn use_is_exact() -> Result<(), ReqParseError> {\r
+ /// assert!(VersionReq::parse("=1.0.0")?.is_exact());\r
+ /// assert!(!VersionReq::parse("=1.0")?.is_exact());\r
+ /// assert!(!VersionReq::parse(">=1.0.0")?.is_exact());\r
+ /// Ok(())\r
+ /// }\r
+ ///\r
+ /// use_is_exact().unwrap();\r
+ /// ```\r
+ pub fn is_exact(&self) -> bool {\r
+ if let [range] = self.ranges.as_slice() {\r
+ if let [predicate] = range.predicates.as_slice() {\r
+ return predicate.has_exactly_one_match();\r
+ }\r
+ }\r
+\r
+ false\r
+ }\r
+}\r
+\r
+impl str::FromStr for VersionReq {\r
+ type Err = ReqParseError;\r
+\r
+ fn from_str(s: &str) -> Result<VersionReq, ReqParseError> {\r
+ VersionReq::parse(s)\r
+ }\r
+}\r
+\r
+impl Range {\r
+ fn matches(&self, ver: &Version) -> bool {\r
+ self.predicates.iter().all(|p| p.matches(ver))\r
+ }\r
+\r
+ fn pre_tag_is_compatible(&self, ver: &Version) -> bool {\r
+ self.predicates.iter().any(|p| p.pre_tag_is_compatible(ver))\r
+ }\r
+}\r
+\r
+impl Predicate {\r
+ fn exact(version: &Version) -> Predicate {\r
+ Predicate {\r
+ op: Ex,\r
+ major: version.major,\r
+ minor: version.minor,\r
+ patch: version.patch,\r
+ pre: version.pre.clone(),\r
+ }\r
+ }\r
+\r
+ /// `matches()` takes a `Version` and determines if it matches this particular `Predicate`.\r
+ pub fn matches(&self, ver: &Version) -> bool {\r
+ match self.op {\r
+ Ex => self.matches_exact(ver),\r
+ Gt => self.matches_greater(ver),\r
+ GtEq => self.matches_exact(ver) || self.matches_greater(ver),\r
+ Lt => !self.matches_exact(ver) && !self.matches_greater(ver),\r
+ LtEq => !self.matches_greater(ver),\r
+ }\r
+ }\r
+\r
+ fn matches_exact(&self, ver: &Version) -> bool {\r
+ self.major == ver.major\r
+ && self.minor == ver.minor\r
+ && self.patch == ver.patch\r
+ && self.pre == ver.pre\r
+ }\r
+\r
+ // https://docs.npmjs.com/misc/semver#prerelease-tags\r
+ fn pre_tag_is_compatible(&self, ver: &Version) -> bool {\r
+ // If a version has a prerelease tag (for example, 1.2.3-alpha.3) then it will\r
+ // only be\r
+ // allowed to satisfy comparator sets if at least one comparator with the same\r
+ // [major,\r
+ // minor, patch] tuple also has a prerelease tag.\r
+ !ver.is_prerelease()\r
+ || (self.major == ver.major\r
+ && self.minor == ver.minor\r
+ && self.patch == ver.patch\r
+ && !self.pre.is_empty())\r
+ }\r
+\r
+ fn matches_greater(&self, ver: &Version) -> bool {\r
+ if self.major != ver.major {\r
+ return ver.major > self.major;\r
+ }\r
+\r
+ if self.minor != ver.minor {\r
+ return ver.minor > self.minor;\r
+ }\r
+\r
+ if self.patch != ver.patch {\r
+ return ver.patch > self.patch;\r
+ }\r
+\r
+ if !self.pre.is_empty() {\r
+ return ver.pre.is_empty() || ver.pre > self.pre;\r
+ }\r
+\r
+ false\r
+ }\r
+\r
+ fn has_exactly_one_match(&self) -> bool {\r
+ self.op == Ex\r
+ }\r
+}\r
+\r
+impl fmt::Display for VersionReq {\r
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {\r
+ if self.ranges.is_empty() {\r
+ write!(fmt, "*")?;\r
+ } else {\r
+ for (i, ref pred) in self.ranges.iter().enumerate() {\r
+ if i == 0 {\r
+ write!(fmt, "{}", pred)?;\r
+ } else {\r
+ write!(fmt, " || {}", pred)?;\r
+ }\r
+ }\r
+ }\r
+\r
+ Ok(())\r
+ }\r
+}\r
+\r
+impl fmt::Display for Range {\r
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {\r
+ for (i, ref pred) in self.predicates.iter().enumerate() {\r
+ if i == 0 {\r
+ write!(fmt, "{}", pred)?;\r
+ } else if self.compat == Compat::Npm {\r
+ // Node does not expect commas between predicates\r
+ write!(fmt, " {}", pred)?;\r
+ } else {\r
+ write!(fmt, ", {}", pred)?;\r
+ }\r
+ }\r
+ Ok(())\r
+ }\r
+}\r
+\r
+impl fmt::Display for Predicate {\r
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {\r
+ write!(\r
+ fmt,\r
+ "{}{}.{}.{}",\r
+ self.op, self.major, self.minor, self.patch\r
+ )?;\r
+\r
+ if !self.pre.is_empty() {\r
+ write!(fmt, "-")?;\r
+ for (i, x) in self.pre.iter().enumerate() {\r
+ if i != 0 {\r
+ write!(fmt, ".")?\r
+ }\r
+ write!(fmt, "{}", x)?;\r
+ }\r
+ }\r
+\r
+ Ok(())\r
+ }\r
+}\r
+\r
+impl fmt::Display for Op {\r
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {\r
+ match *self {\r
+ Ex => write!(fmt, "=")?,\r
+ Gt => write!(fmt, ">")?,\r
+ GtEq => write!(fmt, ">=")?,\r
+ Lt => write!(fmt, "<")?,\r
+ LtEq => write!(fmt, "<=")?,\r
+ }\r
+ Ok(())\r
+ }\r
+}\r
+\r
+#[cfg(test)]\r
+mod test {\r
+ use super::super::version::Version;\r
+ use super::{Compat, Op, VersionReq};\r
+ use std::hash::{Hash, Hasher};\r
+\r
+ fn req(s: &str) -> VersionReq {\r
+ VersionReq::parse(s).unwrap()\r
+ }\r
+\r
+ fn req_npm(s: &str) -> VersionReq {\r
+ VersionReq::parse_compat(s, Compat::Npm).unwrap()\r
+ }\r
+\r
+ fn version(s: &str) -> Version {\r
+ match Version::parse(s) {\r
+ Ok(v) => v,\r
+ Err(e) => panic!("`{}` is not a valid version. Reason: {:?}", s, e),\r
+ }\r
+ }\r
+\r
+ fn assert_match(req: &VersionReq, vers: &[&str]) {\r
+ for ver in vers.iter() {\r
+ assert!(req.matches(&version(*ver)), "did not match {}", ver);\r
+ }\r
+ }\r
+\r
+ fn assert_not_match(req: &VersionReq, vers: &[&str]) {\r
+ for ver in vers.iter() {\r
+ assert!(!req.matches(&version(*ver)), "matched {}", ver);\r
+ }\r
+ }\r
+\r
+ fn calculate_hash<T: Hash>(t: T) -> u64 {\r
+ use std::collections::hash_map::DefaultHasher;\r
+\r
+ let mut s = DefaultHasher::new();\r
+ t.hash(&mut s);\r
+ s.finish()\r
+ }\r
+\r
+ #[test]\r
+ fn test_parsing_default() {\r
+ let r = req("1.0.0");\r
+\r
+ assert_eq!(r.to_string(), ">=1.0.0, <2.0.0".to_string());\r
+\r
+ assert_match(&r, &["1.0.0", "1.0.1"]);\r
+ assert_not_match(&r, &["0.9.9", "0.10.0", "0.1.0"]);\r
+ }\r
+\r
+ #[test]\r
+ fn test_parsing_default_npm() {\r
+ let r = req_npm("1.0.0");\r
+\r
+ assert_eq!(r.to_string(), "=1.0.0".to_string());\r
+\r
+ assert_match(&r, &["1.0.0"]);\r
+ assert_not_match(&r, &["0.9.9", "0.10.0", "0.1.0", "1.0.1"]);\r
+ }\r
+\r
+ #[test]\r
+ fn test_parsing_exact() {\r
+ let r = req("=1.0.0");\r
+\r
+ assert!(r.to_string() == "=1.0.0".to_string());\r
+ assert_eq!(r.to_string(), "=1.0.0".to_string());\r
+\r
+ assert_match(&r, &["1.0.0"]);\r
+ assert_not_match(&r, &["1.0.1", "0.9.9", "0.10.0", "0.1.0", "1.0.0-pre"]);\r
+\r
+ let r = req("=0.9.0");\r
+\r
+ assert_eq!(r.to_string(), "=0.9.0".to_string());\r
+\r
+ assert_match(&r, &["0.9.0"]);\r
+ assert_not_match(&r, &["0.9.1", "1.9.0", "0.0.9"]);\r
+\r
+ let r = req("=0.1.0-beta2.a");\r
+\r
+ assert_eq!(r.to_string(), "=0.1.0-beta2.a".to_string());\r
+\r
+ assert_match(&r, &["0.1.0-beta2.a"]);\r
+ assert_not_match(&r, &["0.9.1", "0.1.0", "0.1.1-beta2.a", "0.1.0-beta2"]);\r
+ }\r
+\r
+ #[test]\r
+ fn test_parse_metadata_see_issue_88_see_issue_88() {\r
+ for op in &[Op::Ex, Op::Gt, Op::GtEq, Op::Lt, Op::LtEq] {\r
+ println!("{} 1.2.3+meta", op);\r
+ req(&format!("{} 1.2.3+meta", op));\r
+ }\r
+ }\r
+\r
+ #[test]\r
+ pub fn test_parsing_greater_than() {\r
+ let r = req(">= 1.0.0");\r
+\r
+ assert_eq!(r.to_string(), ">=1.0.0".to_string());\r
+\r
+ assert_match(&r, &["1.0.0", "2.0.0"]);\r
+ assert_not_match(&r, &["0.1.0", "0.0.1", "1.0.0-pre", "2.0.0-pre"]);\r
+\r
+ // https://github.com/steveklabnik/semver/issues/53\r
+ let r = req(">= 2.1.0-alpha2");\r
+\r
+ assert_match(&r, &["2.1.0-alpha2", "2.1.0-alpha3", "2.1.0", "3.0.0"]);\r
+ assert_not_match(\r
+ &r,\r
+ &["2.0.0", "2.1.0-alpha1", "2.0.0-alpha2", "3.0.0-alpha2"],\r
+ );\r
+ }\r
+\r
+ #[test]\r
+ pub fn test_parsing_less_than() {\r
+ let r = req("< 1.0.0");\r
+\r
+ assert_eq!(r.to_string(), "<1.0.0".to_string());\r
+\r
+ assert_match(&r, &["0.1.0", "0.0.1"]);\r
+ assert_not_match(&r, &["1.0.0", "1.0.0-beta", "1.0.1", "0.9.9-alpha"]);\r
+\r
+ let r = req("<= 2.1.0-alpha2");\r
+\r
+ assert_match(&r, &["2.1.0-alpha2", "2.1.0-alpha1", "2.0.0", "1.0.0"]);\r
+ assert_not_match(\r
+ &r,\r
+ &["2.1.0", "2.2.0-alpha1", "2.0.0-alpha2", "1.0.0-alpha2"],\r
+ );\r
+ }\r
+\r
+ #[test]\r
+ pub fn test_multiple() {\r
+ let r = req("> 0.0.9, <= 2.5.3");\r
+ assert_eq!(r.to_string(), ">0.0.9, <=2.5.3".to_string());\r
+ assert_match(&r, &["0.0.10", "1.0.0", "2.5.3"]);\r
+ assert_not_match(&r, &["0.0.8", "2.5.4"]);\r
+\r
+ let r = req("0.3.0, 0.4.0");\r
+ assert_eq!(\r
+ r.to_string(),\r
+ ">=0.3.0, <0.4.0, >=0.4.0, <0.5.0".to_string()\r
+ );\r
+ assert_not_match(&r, &["0.0.8", "0.3.0", "0.4.0"]);\r
+\r
+ let r = req("<= 0.2.0, >= 0.5.0");\r
+ assert_eq!(r.to_string(), "<=0.2.0, >=0.5.0".to_string());\r
+ assert_not_match(&r, &["0.0.8", "0.3.0", "0.5.1"]);\r
+\r
+ let r = req("0.1.0, 0.1.4, 0.1.6");\r
+ assert_eq!(\r
+ r.to_string(),\r
+ ">=0.1.0, <0.2.0, >=0.1.4, <0.2.0, >=0.1.6, <0.2.0".to_string()\r
+ );\r
+ assert_match(&r, &["0.1.6", "0.1.9"]);\r
+ assert_not_match(&r, &["0.1.0", "0.1.4", "0.2.0"]);\r
+\r
+ assert!(VersionReq::parse("> 0.1.0,").is_err());\r
+ assert!(VersionReq::parse("> 0.3.0, ,").is_err());\r
+\r
+ let r = req(">=0.5.1-alpha3, <0.6");\r
+ assert_eq!(r.to_string(), ">=0.5.1-alpha3, <0.6.0".to_string());\r
+ assert_match(\r
+ &r,\r
+ &[\r
+ "0.5.1-alpha3",\r
+ "0.5.1-alpha4",\r
+ "0.5.1-beta",\r
+ "0.5.1",\r
+ "0.5.5",\r
+ ],\r
+ );\r
+ assert_not_match(\r
+ &r,\r
+ &["0.5.1-alpha1", "0.5.2-alpha3", "0.5.5-pre", "0.5.0-pre"],\r
+ );\r
+ assert_not_match(&r, &["0.6.0", "0.6.0-pre"]);\r
+\r
+ // https://github.com/steveklabnik/semver/issues/56\r
+ let r = req("1.2.3 - 2.3.4");\r
+ assert_eq!(r.to_string(), ">=1.2.3, <=2.3.4");\r
+ assert_match(&r, &["1.2.3", "1.2.10", "2.0.0", "2.3.4"]);\r
+ assert_not_match(&r, &["1.0.0", "1.2.2", "1.2.3-alpha1", "2.3.5"]);\r
+ }\r
+\r
+ // https://github.com/steveklabnik/semver/issues/55\r
+ #[test]\r
+ pub fn test_whitespace_delimited_comparator_sets() {\r
+ let r = req("> 0.0.9 <= 2.5.3");\r
+ assert_eq!(r.to_string(), ">0.0.9, <=2.5.3".to_string());\r
+ assert_match(&r, &["0.0.10", "1.0.0", "2.5.3"]);\r
+ assert_not_match(&r, &["0.0.8", "2.5.4"]);\r
+ }\r
+\r
+ #[test]\r
+ pub fn test_multiple_npm() {\r
+ let r = req_npm("> 0.0.9, <= 2.5.3");\r
+ assert_eq!(r.to_string(), ">0.0.9 <=2.5.3".to_string());\r
+ assert_match(&r, &["0.0.10", "1.0.0", "2.5.3"]);\r
+ assert_not_match(&r, &["0.0.8", "2.5.4"]);\r
+\r
+ let r = req_npm("0.3.0, 0.4.0");\r
+ assert_eq!(r.to_string(), "=0.3.0 =0.4.0".to_string());\r
+ assert_not_match(&r, &["0.0.8", "0.3.0", "0.4.0"]);\r
+\r
+ let r = req_npm("<= 0.2.0, >= 0.5.0");\r
+ assert_eq!(r.to_string(), "<=0.2.0 >=0.5.0".to_string());\r
+ assert_not_match(&r, &["0.0.8", "0.3.0", "0.5.1"]);\r
+\r
+ let r = req_npm("0.1.0, 0.1.4, 0.1.6");\r
+ assert_eq!(r.to_string(), "=0.1.0 =0.1.4 =0.1.6".to_string());\r
+ assert_not_match(&r, &["0.1.0", "0.1.4", "0.1.6", "0.2.0"]);\r
+\r
+ assert!(VersionReq::parse("> 0.1.0,").is_err());\r
+ assert!(VersionReq::parse("> 0.3.0, ,").is_err());\r
+\r
+ let r = req_npm(">=0.5.1-alpha3, <0.6");\r
+ assert_eq!(r.to_string(), ">=0.5.1-alpha3 <0.6.0".to_string());\r
+ assert_match(\r
+ &r,\r
+ &[\r
+ "0.5.1-alpha3",\r
+ "0.5.1-alpha4",\r
+ "0.5.1-beta",\r
+ "0.5.1",\r
+ "0.5.5",\r
+ ],\r
+ );\r
+ assert_not_match(\r
+ &r,\r
+ &["0.5.1-alpha1", "0.5.2-alpha3", "0.5.5-pre", "0.5.0-pre"],\r
+ );\r
+ assert_not_match(&r, &["0.6.0", "0.6.0-pre"]);\r
+ }\r
+\r
+ #[test]\r
+ pub fn test_parsing_tilde() {\r
+ let r = req("~1");\r
+ assert_match(&r, &["1.0.0", "1.0.1", "1.1.1"]);\r
+ assert_not_match(&r, &["0.9.1", "2.9.0", "0.0.9"]);\r
+\r
+ let r = req("~1.2");\r
+ assert_match(&r, &["1.2.0", "1.2.1"]);\r
+ assert_not_match(&r, &["1.1.1", "1.3.0", "0.0.9"]);\r
+\r
+ let r = req("~1.2.2");\r
+ assert_match(&r, &["1.2.2", "1.2.4"]);\r
+ assert_not_match(&r, &["1.2.1", "1.9.0", "1.0.9", "2.0.1", "0.1.3"]);\r
+\r
+ let r = req("~1.2.3-beta.2");\r
+ assert_match(&r, &["1.2.3", "1.2.4", "1.2.3-beta.2", "1.2.3-beta.4"]);\r
+ assert_not_match(&r, &["1.3.3", "1.1.4", "1.2.3-beta.1", "1.2.4-beta.2"]);\r
+ }\r
+\r
+ #[test]\r
+ pub fn test_parsing_compatible() {\r
+ let r = req("^1");\r
+ assert_match(&r, &["1.1.2", "1.1.0", "1.2.1", "1.0.1"]);\r
+ assert_not_match(&r, &["0.9.1", "2.9.0", "0.1.4"]);\r
+ assert_not_match(&r, &["1.0.0-beta1", "0.1.0-alpha", "1.0.1-pre"]);\r
+\r
+ let r = req("^1.1");\r
+ assert_match(&r, &["1.1.2", "1.1.0", "1.2.1"]);\r
+ assert_not_match(&r, &["0.9.1", "2.9.0", "1.0.1", "0.1.4"]);\r
+\r
+ let r = req("^1.1.2");\r
+ assert_match(&r, &["1.1.2", "1.1.4", "1.2.1"]);\r
+ assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1"]);\r
+ assert_not_match(&r, &["1.1.2-alpha1", "1.1.3-alpha1", "2.9.0-alpha1"]);\r
+\r
+ let r = req("^0.1.2");\r
+ assert_match(&r, &["0.1.2", "0.1.4"]);\r
+ assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1"]);\r
+ assert_not_match(&r, &["0.1.2-beta", "0.1.3-alpha", "0.2.0-pre"]);\r
+\r
+ let r = req("^0.5.1-alpha3");\r
+ assert_match(\r
+ &r,\r
+ &[\r
+ "0.5.1-alpha3",\r
+ "0.5.1-alpha4",\r
+ "0.5.1-beta",\r
+ "0.5.1",\r
+ "0.5.5",\r
+ ],\r
+ );\r
+ assert_not_match(\r
+ &r,\r
+ &[\r
+ "0.5.1-alpha1",\r
+ "0.5.2-alpha3",\r
+ "0.5.5-pre",\r
+ "0.5.0-pre",\r
+ "0.6.0",\r
+ ],\r
+ );\r
+\r
+ let r = req("^0.0.2");\r
+ assert_match(&r, &["0.0.2"]);\r
+ assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1", "0.1.4"]);\r
+\r
+ let r = req("^0.0");\r
+ assert_match(&r, &["0.0.2", "0.0.0"]);\r
+ assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.1.4"]);\r
+\r
+ let r = req("^0");\r
+ assert_match(&r, &["0.9.1", "0.0.2", "0.0.0"]);\r
+ assert_not_match(&r, &["2.9.0", "1.1.1"]);\r
+\r
+ let r = req("^1.4.2-beta.5");\r
+ assert_match(\r
+ &r,\r
+ &["1.4.2", "1.4.3", "1.4.2-beta.5", "1.4.2-beta.6", "1.4.2-c"],\r
+ );\r
+ assert_not_match(\r
+ &r,\r
+ &[\r
+ "0.9.9",\r
+ "2.0.0",\r
+ "1.4.2-alpha",\r
+ "1.4.2-beta.4",\r
+ "1.4.3-beta.5",\r
+ ],\r
+ );\r
+ }\r
+\r
+ #[test]\r
+ pub fn test_parsing_wildcard() {\r
+ let r = req("");\r
+ assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);\r
+ assert_not_match(&r, &[]);\r
+ let r = req("*");\r
+ assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);\r
+ assert_not_match(&r, &[]);\r
+ let r = req("x");\r
+ assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);\r
+ assert_not_match(&r, &[]);\r
+ let r = req("X");\r
+ assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);\r
+ assert_not_match(&r, &[]);\r
+\r
+ let r = req("1.*");\r
+ assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]);\r
+ assert_not_match(&r, &["0.0.9"]);\r
+ let r = req("1.x");\r
+ assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]);\r
+ assert_not_match(&r, &["0.0.9"]);\r
+ let r = req("1.X");\r
+ assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]);\r
+ assert_not_match(&r, &["0.0.9"]);\r
+\r
+ let r = req("1.2.*");\r
+ assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]);\r
+ assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]);\r
+ let r = req("1.2.x");\r
+ assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]);\r
+ assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]);\r
+ let r = req("1.2.X");\r
+ assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]);\r
+ assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]);\r
+ }\r
+\r
+ // https://github.com/steveklabnik/semver/issues/57\r
+ #[test]\r
+ pub fn test_parsing_logical_or() {\r
+ let r = req("=1.2.3 || =2.3.4");\r
+ assert_eq!(r.to_string(), "=1.2.3 || =2.3.4".to_string());\r
+ assert_match(&r, &["1.2.3", "2.3.4"]);\r
+ assert_not_match(&r, &["1.0.0", "2.9.0", "0.1.4"]);\r
+ assert_not_match(&r, &["1.2.3-beta1", "2.3.4-alpha", "1.2.3-pre"]);\r
+\r
+ let r = req("1.1 || =1.2.3");\r
+ assert_eq!(r.to_string(), ">=1.1.0, <1.2.0 || =1.2.3".to_string());\r
+ assert_match(&r, &["1.1.0", "1.1.12", "1.2.3"]);\r
+ assert_not_match(&r, &["1.0.0", "1.2.2", "1.3.0"]);\r
+\r
+ let r = req("6.* || 8.* || >= 10.*");\r
+ assert_eq!(\r
+ r.to_string(),\r
+ ">=6.0.0, <7.0.0 || >=8.0.0, <9.0.0 || >=10.0.0".to_string()\r
+ );\r
+ assert_match(&r, &["6.0.0", "6.1.2"]);\r
+ assert_match(&r, &["8.0.0", "8.2.4"]);\r
+ assert_match(&r, &["10.1.2", "11.3.4"]);\r
+ assert_not_match(&r, &["5.0.0", "7.0.0", "9.0.0"]);\r
+ }\r
+\r
+ #[test]\r
+ pub fn test_parsing_logical_or_npm() {\r
+ let r = req_npm("=1.2.3 || =2.3.4");\r
+ assert_eq!(r.to_string(), "=1.2.3 || =2.3.4".to_string());\r
+ assert_match(&r, &["1.2.3", "2.3.4"]);\r
+ assert_not_match(&r, &["1.0.0", "2.9.0", "0.1.4"]);\r
+ assert_not_match(&r, &["1.2.3-beta1", "2.3.4-alpha", "1.2.3-pre"]);\r
+\r
+ let r = req_npm("1.1 || =1.2.3");\r
+ assert_eq!(r.to_string(), ">=1.1.0 <1.2.0 || =1.2.3".to_string());\r
+ assert_match(&r, &["1.1.0", "1.1.12", "1.2.3"]);\r
+ assert_not_match(&r, &["1.0.0", "1.2.2", "1.3.0"]);\r
+\r
+ let r = req_npm("6.* || 8.* || >= 10.*");\r
+ assert_eq!(\r
+ r.to_string(),\r
+ ">=6.0.0 <7.0.0 || >=8.0.0 <9.0.0 || >=10.0.0".to_string()\r
+ );\r
+ assert_match(&r, &["6.0.0", "6.1.2"]);\r
+ assert_match(&r, &["8.0.0", "8.2.4"]);\r
+ assert_match(&r, &["10.1.2", "11.3.4"]);\r
+ assert_not_match(&r, &["5.0.0", "7.0.0", "9.0.0"]);\r
+ }\r
+\r
+ #[test]\r
+ pub fn test_any() {\r
+ let r = VersionReq::any();\r
+ assert_match(&r, &["0.0.1", "0.1.0", "1.0.0"]);\r
+ }\r
+\r
+ #[test]\r
+ pub fn test_pre() {\r
+ let r = req("=2.1.1-really.0");\r
+ assert_match(&r, &["2.1.1-really.0"]);\r
+ }\r
+\r
+ // #[test]\r
+ // pub fn test_parse_errors() {\r
+ // assert_eq!(Err(InvalidVersionRequirement), VersionReq::parse("\0"));\r
+ // assert_eq!(Err(OpAlreadySet), VersionReq::parse(">= >= 0.0.2"));\r
+ // assert_eq!(Err(InvalidSigil), VersionReq::parse(">== 0.0.2"));\r
+ // assert_eq!(Err(VersionComponentsMustBeNumeric),\r
+ // VersionReq::parse("a.0.0"));\r
+ // assert_eq!(Err(InvalidIdentifier), VersionReq::parse("1.0.0-"));\r
+ // assert_eq!(Err(MajorVersionRequired), VersionReq::parse(">="));\r
+ // }\r
+\r
+ #[test]\r
+ pub fn test_from_str() {\r
+ assert_eq!(\r
+ "1.0.0".parse::<VersionReq>().unwrap().to_string(),\r
+ ">=1.0.0, <2.0.0".to_string()\r
+ );\r
+ assert_eq!(\r
+ "=1.0.0".parse::<VersionReq>().unwrap().to_string(),\r
+ "=1.0.0".to_string()\r
+ );\r
+ assert_eq!(\r
+ "~1".parse::<VersionReq>().unwrap().to_string(),\r
+ ">=1.0.0, <2.0.0".to_string()\r
+ );\r
+ assert_eq!(\r
+ "~1.2".parse::<VersionReq>().unwrap().to_string(),\r
+ ">=1.2.0, <1.3.0".to_string()\r
+ );\r
+ assert_eq!(\r
+ "^1".parse::<VersionReq>().unwrap().to_string(),\r
+ ">=1.0.0, <2.0.0".to_string()\r
+ );\r
+ assert_eq!(\r
+ "^1.1".parse::<VersionReq>().unwrap().to_string(),\r
+ ">=1.1.0, <2.0.0".to_string()\r
+ );\r
+ assert_eq!(\r
+ "*".parse::<VersionReq>().unwrap().to_string(),\r
+ ">=0.0.0".to_string()\r
+ );\r
+ assert_eq!(\r
+ "1.*".parse::<VersionReq>().unwrap().to_string(),\r
+ ">=1.0.0, <2.0.0".to_string()\r
+ );\r
+ assert_eq!(\r
+ "< 1.0.0".parse::<VersionReq>().unwrap().to_string(),\r
+ "<1.0.0".to_string()\r
+ );\r
+ }\r
+\r
+ // #[test]\r
+ // pub fn test_from_str_errors() {\r
+ // assert_eq!(Err(InvalidVersionRequirement), "\0".parse::<VersionReq>());\r
+ // assert_eq!(Err(OpAlreadySet), ">= >= 0.0.2".parse::<VersionReq>());\r
+ // assert_eq!(Err(InvalidSigil), ">== 0.0.2".parse::<VersionReq>());\r
+ // assert_eq!(Err(VersionComponentsMustBeNumeric),\r
+ // "a.0.0".parse::<VersionReq>());\r
+ // assert_eq!(Err(InvalidIdentifier), "1.0.0-".parse::<VersionReq>());\r
+ // assert_eq!(Err(MajorVersionRequired), ">=".parse::<VersionReq>());\r
+ // }\r
+\r
+ #[test]\r
+ fn test_cargo3202() {\r
+ let v = "0.*.*".parse::<VersionReq>().unwrap();\r
+ assert_eq!(">=0.0.0, <1.0.0", format!("{}", v.ranges[0]));\r
+\r
+ let v = "0.0.*".parse::<VersionReq>().unwrap();\r
+ assert_eq!(">=0.0.0, <0.1.0", format!("{}", v.ranges[0]));\r
+\r
+ let r = req("0.*.*");\r
+ assert_match(&r, &["0.5.0"]);\r
+ }\r
+\r
+ #[test]\r
+ fn test_eq_hash() {\r
+ assert!(req("^1") == req("^1"));\r
+ assert!(calculate_hash(req("^1")) == calculate_hash(req("^1")));\r
+ assert!(req("^1") != req("^2"));\r
+ }\r
+\r
+ #[test]\r
+ fn test_ordering() {\r
+ assert!(req("=1") > req("*"));\r
+ assert!(req(">1") < req("*"));\r
+ assert!(req(">=1") > req("*"));\r
+ assert!(req("<1") > req("*"));\r
+ assert!(req("<=1") > req("*"));\r
+ assert!(req("~1") > req("*"));\r
+ assert!(req("^1") > req("*"));\r
+ assert!(req("*") == req("*"));\r
+ }\r
+\r
+ #[test]\r
+ fn is_exact() {\r
+ assert!(req("=1.0.0").is_exact());\r
+ assert!(req("=1.0.0-alpha").is_exact());\r
+\r
+ assert!(!req("=1").is_exact());\r
+ assert!(!req(">=1.0.0").is_exact());\r
+ assert!(!req(">=1.0.0, <2.0.0").is_exact());\r
+ }\r
+}\r