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