]> git.proxmox.com Git - rustc.git/blame - vendor/semver/src/version_req.rs
New upstream version 1.52.1+dfsg1
[rustc.git] / vendor / semver / src / version_req.rs
CommitLineData
f20569fa
XL
1// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT\r
2// file at the top-level directory of this distribution and at\r
3// http://rust-lang.org/COPYRIGHT.\r
4//\r
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or\r
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license\r
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your\r
8// option. This file may not be copied, modified, or distributed\r
9// except according to those terms.\r
10\r
11use std::error::Error;\r
12use std::fmt;\r
13use std::str;\r
14\r
15use semver_parser;\r
16use semver_parser::{Compat, RangeSet};\r
17use version::Identifier;\r
18use Version;\r
19\r
20#[cfg(feature = "serde")]\r
21use serde::de::{self, Deserialize, Deserializer, Visitor};\r
22#[cfg(feature = "serde")]\r
23use serde::ser::{Serialize, Serializer};\r
24\r
25use self::Op::{Ex, Gt, GtEq, Lt, LtEq};\r
26use self::ReqParseError::*;\r
27\r
28/// A `VersionReq` is a struct containing a list of ranges that can apply to ranges of version\r
29/// numbers. Matching operations can then be done with the `VersionReq` against a particular\r
30/// version to see if it satisfies some or all of the constraints.\r
31#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]\r
32#[cfg_attr(feature = "diesel", derive(AsExpression, FromSqlRow))]\r
33#[cfg_attr(feature = "diesel", sql_type = "diesel::sql_types::Text")]\r
34pub struct VersionReq {\r
35 ranges: Vec<Range>,\r
36 compat: Compat, // defaults to Cargo\r
37}\r
38\r
39impl From<semver_parser::RangeSet> for VersionReq {\r
40 fn from(range_set: semver_parser::RangeSet) -> VersionReq {\r
41 VersionReq {\r
42 ranges: range_set.ranges.into_iter().map(From::from).collect(),\r
43 compat: range_set.compat,\r
44 }\r
45 }\r
46}\r
47\r
48#[cfg(feature = "serde")]\r
49impl Serialize for VersionReq {\r
50 fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>\r
51 where\r
52 S: Serializer,\r
53 {\r
54 // Serialize VersionReq as a string.\r
55 serializer.collect_str(self)\r
56 }\r
57}\r
58\r
59// TODO: how to implement deserialize with compatibility?\r
60#[cfg(feature = "serde")]\r
61impl<'de> Deserialize<'de> for VersionReq {\r
62 fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>\r
63 where\r
64 D: Deserializer<'de>,\r
65 {\r
66 struct VersionReqVisitor;\r
67\r
68 /// Deserialize `VersionReq` from a string.\r
69 impl<'de> Visitor<'de> for VersionReqVisitor {\r
70 type Value = VersionReq;\r
71\r
72 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\r
73 formatter.write_str("a SemVer version requirement as a string")\r
74 }\r
75\r
76 fn visit_str<E>(self, v: &str) -> ::std::result::Result<Self::Value, E>\r
77 where\r
78 E: de::Error,\r
79 {\r
80 VersionReq::parse(v).map_err(de::Error::custom)\r
81 }\r
82 }\r
83\r
84 deserializer.deserialize_str(VersionReqVisitor)\r
85 }\r
86}\r
87\r
88#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]\r
89enum Op {\r
90 Ex, // Exact\r
91 Gt, // Greater than\r
92 GtEq, // Greater than or equal to\r
93 Lt, // Less than\r
94 LtEq, // Less than or equal to\r
95}\r
96\r
97impl From<semver_parser::Op> for Op {\r
98 fn from(op: semver_parser::Op) -> Op {\r
99 match op {\r
100 semver_parser::Op::Eq => Op::Ex,\r
101 semver_parser::Op::Gt => Op::Gt,\r
102 semver_parser::Op::Gte => Op::GtEq,\r
103 semver_parser::Op::Lt => Op::Lt,\r
104 semver_parser::Op::Lte => Op::LtEq,\r
105 }\r
106 }\r
107}\r
108\r
109#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]\r
110struct Range {\r
111 predicates: Vec<Predicate>,\r
112 compat: Compat,\r
113}\r
114\r
115impl From<semver_parser::Range> for Range {\r
116 fn from(range: semver_parser::Range) -> Range {\r
117 Range {\r
118 predicates: range.comparator_set.into_iter().map(From::from).collect(),\r
119 compat: range.compat,\r
120 }\r
121 }\r
122}\r
123\r
124#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]\r
125struct Predicate {\r
126 op: Op,\r
127 major: u64,\r
128 minor: u64,\r
129 patch: u64,\r
130 pre: Vec<Identifier>,\r
131}\r
132\r
133impl From<semver_parser::Comparator> for Predicate {\r
134 fn from(comparator: semver_parser::Comparator) -> Predicate {\r
135 Predicate {\r
136 op: From::from(comparator.op),\r
137 major: comparator.major,\r
138 minor: comparator.minor,\r
139 patch: comparator.patch,\r
140 pre: comparator.pre.into_iter().map(From::from).collect(),\r
141 }\r
142 }\r
143}\r
144\r
145impl From<semver_parser::Identifier> for Identifier {\r
146 fn from(identifier: semver_parser::Identifier) -> Identifier {\r
147 match identifier {\r
148 semver_parser::Identifier::Numeric(n) => Identifier::Numeric(n),\r
149 semver_parser::Identifier::AlphaNumeric(s) => Identifier::AlphaNumeric(s),\r
150 }\r
151 }\r
152}\r
153\r
154/// A `ReqParseError` is returned from methods which parse a string into a [`VersionReq`]. Each\r
155/// enumeration is one of the possible errors that can occur.\r
156/// [`VersionReq`]: struct.VersionReq.html\r
157#[derive(Clone, Debug, PartialEq)]\r
158pub enum ReqParseError {\r
159 /// The given version requirement is invalid.\r
160 InvalidVersionRequirement,\r
161 /// You have already provided an operation, such as `=`, `~`, or `^`. Only use one.\r
162 OpAlreadySet,\r
163 /// The sigil you have written is not correct.\r
164 InvalidSigil,\r
165 /// All components of a version must be numeric.\r
166 VersionComponentsMustBeNumeric,\r
167 /// There was an error parsing an identifier.\r
168 InvalidIdentifier,\r
169 /// At least a major version is required.\r
170 MajorVersionRequired,\r
171 /// An unimplemented version requirement.\r
172 UnimplementedVersionRequirement,\r
173 /// This form of requirement is deprecated.\r
174 DeprecatedVersionRequirement(VersionReq),\r
175}\r
176\r
177impl fmt::Display for ReqParseError {\r
178 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\r
179 let msg = match self {\r
180 InvalidVersionRequirement => "the given version requirement is invalid",\r
181 OpAlreadySet => {\r
182 "you have already provided an operation, such as =, ~, or ^; only use one"\r
183 }\r
184 InvalidSigil => "the sigil you have written is not correct",\r
185 VersionComponentsMustBeNumeric => "version components must be numeric",\r
186 InvalidIdentifier => "invalid identifier",\r
187 MajorVersionRequired => "at least a major version number is required",\r
188 UnimplementedVersionRequirement => {\r
189 "the given version requirement is not implemented, yet"\r
190 }\r
191 DeprecatedVersionRequirement(_) => "This requirement is deprecated",\r
192 };\r
193 msg.fmt(f)\r
194 }\r
195}\r
196\r
197impl Error for ReqParseError {}\r
198\r
199impl From<String> for ReqParseError {\r
200 fn from(other: String) -> ReqParseError {\r
201 match &*other {\r
202 "Null is not a valid VersionReq" => ReqParseError::InvalidVersionRequirement,\r
203 "VersionReq did not parse properly." => ReqParseError::OpAlreadySet,\r
204 _ => ReqParseError::InvalidVersionRequirement,\r
205 }\r
206 }\r
207}\r
208\r
209impl VersionReq {\r
210 /// `any()` is a factory method which creates a `VersionReq` with no constraints. In other\r
211 /// words, any version will match against it.\r
212 ///\r
213 /// # Examples\r
214 ///\r
215 /// ```\r
216 /// use semver::VersionReq;\r
217 ///\r
218 /// let anything = VersionReq::any();\r
219 /// ```\r
220 pub fn any() -> VersionReq {\r
221 VersionReq {\r
222 ranges: vec![],\r
223 compat: Compat::Cargo,\r
224 }\r
225 }\r
226\r
227 /// `parse()` is the main constructor of a `VersionReq`. It takes a string like `"^1.2.3"`\r
228 /// and turns it into a `VersionReq` that matches that particular constraint.\r
229 ///\r
230 /// A `Result` is returned which contains a [`ReqParseError`] if there was a problem parsing the\r
231 /// `VersionReq`.\r
232 /// [`ReqParseError`]: enum.ReqParseError.html\r
233 ///\r
234 /// # Examples\r
235 ///\r
236 /// ```\r
237 /// use semver::VersionReq;\r
238 ///\r
239 /// let version = VersionReq::parse("=1.2.3");\r
240 /// let version = VersionReq::parse(">1.2.3");\r
241 /// let version = VersionReq::parse("<1.2.3");\r
242 /// let version = VersionReq::parse("~1.2.3");\r
243 /// let version = VersionReq::parse("^1.2.3");\r
244 /// let version = VersionReq::parse("1.2.3"); // synonym for ^1.2.3\r
245 /// let version = VersionReq::parse("<=1.2.3");\r
246 /// let version = VersionReq::parse(">=1.2.3");\r
247 /// ```\r
248 ///\r
249 /// This example demonstrates error handling, and will panic.\r
250 ///\r
251 /// ```should_panic\r
252 /// use semver::VersionReq;\r
253 ///\r
254 /// let version = match VersionReq::parse("not a version") {\r
255 /// Ok(version) => version,\r
256 /// Err(e) => panic!("There was a problem parsing: {}", e),\r
257 /// };\r
258 /// ```\r
259 ///\r
260 /// # Errors\r
261 ///\r
262 /// Returns an error variant if the input could not be parsed as a semver requirement.\r
263 ///\r
264 /// Examples of common error causes are as follows:\r
265 ///\r
266 /// * `\0` - an invalid version requirement is used.\r
267 /// * `>= >= 1.2.3` - multiple operations are used. Only use one.\r
268 /// * `>== 1.2.3` - an invalid operation is used.\r
269 /// * `a.0.0` - version components are not numeric.\r
270 /// * `1.2.3-` - an invalid identifier is present.\r
271 /// * `>=` - major version was not specified. At least a major version is required.\r
272 /// * `0.2*` - deprecated requirement syntax. Equivalent would be `0.2.*`.\r
273 ///\r
274 /// You may also encounter an `UnimplementedVersionRequirement` error, which indicates that a\r
275 /// given requirement syntax is not yet implemented in this crate.\r
276 pub fn parse(input: &str) -> Result<VersionReq, ReqParseError> {\r
277 let range_set = input.parse::<RangeSet>();\r
278\r
279 if let Ok(v) = range_set {\r
280 return Ok(From::from(v));\r
281 }\r
282\r
283 match VersionReq::parse_deprecated(input) {\r
284 Some(v) => Err(ReqParseError::DeprecatedVersionRequirement(v)),\r
285 None => Err(From::from(range_set.err().unwrap())),\r
286 }\r
287 }\r
288\r
289 // TODO: better docs for this\r
290 /// `parse_compat()` is like `parse()`, but it takes an extra argument for compatibility with\r
291 /// other semver implementations, and turns that into a `VersionReq` that matches the\r
292 /// particular constraint and compatibility.\r
293 ///\r
294 /// A `Result` is returned which contains a [`ReqParseError`] if there was a problem parsing the\r
295 /// `VersionReq`.\r
296 /// [`ReqParseError`]: enum.ReqParseError.html\r
297 ///\r
298 /// # Examples\r
299 ///\r
300 /// ```\r
301 /// extern crate semver_parser;\r
302 /// use semver::VersionReq;\r
303 /// use semver_parser::Compat;\r
304 ///\r
305 /// # fn main() {\r
306 /// let cargo_version = VersionReq::parse_compat("1.2.3", Compat::Cargo);\r
307 /// let npm_version = VersionReq::parse_compat("1.2.3", Compat::Npm);\r
308 /// # }\r
309 /// ```\r
310 pub fn parse_compat(input: &str, compat: Compat) -> Result<VersionReq, ReqParseError> {\r
311 let range_set = RangeSet::parse(input, compat);\r
312\r
313 if let Ok(v) = range_set {\r
314 return Ok(From::from(v));\r
315 }\r
316\r
317 match VersionReq::parse_deprecated(input) {\r
318 Some(v) => Err(ReqParseError::DeprecatedVersionRequirement(v)),\r
319 None => Err(From::from(range_set.err().unwrap())),\r
320 }\r
321 }\r
322\r
323 fn parse_deprecated(version: &str) -> Option<VersionReq> {\r
324 match version {\r
325 ".*" => Some(VersionReq::any()),\r
326 "0.1.0." => Some(VersionReq::parse("0.1.0").unwrap()),\r
327 "0.3.1.3" => Some(VersionReq::parse("0.3.13").unwrap()),\r
328 "0.2*" => Some(VersionReq::parse("0.2.*").unwrap()),\r
329 "*.0" => Some(VersionReq::any()),\r
330 _ => None,\r
331 }\r
332 }\r
333\r
334 /// `exact()` is a factory method which creates a `VersionReq` with one exact constraint.\r
335 ///\r
336 /// # Examples\r
337 ///\r
338 /// ```\r
339 /// use semver::VersionReq;\r
340 /// use semver::Version;\r
341 ///\r
342 /// let version = Version { major: 1, minor: 1, patch: 1, pre: vec![], build: vec![] };\r
343 /// let exact = VersionReq::exact(&version);\r
344 /// ```\r
345 pub fn exact(version: &Version) -> VersionReq {\r
346 VersionReq {\r
347 ranges: vec![Range {\r
348 predicates: vec![Predicate::exact(version)],\r
349 compat: Compat::Cargo,\r
350 }],\r
351 compat: Compat::Cargo,\r
352 }\r
353 }\r
354\r
355 /// `matches()` matches a given [`Version`] against this `VersionReq`.\r
356 /// [`Version`]: struct.Version.html\r
357 ///\r
358 /// # Examples\r
359 ///\r
360 /// ```\r
361 /// use semver::VersionReq;\r
362 /// use semver::Version;\r
363 ///\r
364 /// let version = Version { major: 1, minor: 1, patch: 1, pre: vec![], build: vec![] };\r
365 /// let exact = VersionReq::exact(&version);\r
366 ///\r
367 /// assert!(exact.matches(&version));\r
368 /// ```\r
369 pub fn matches(&self, version: &Version) -> bool {\r
370 // no ranges means anything matches\r
371 if self.ranges.is_empty() {\r
372 return true;\r
373 }\r
374\r
375 self.ranges\r
376 .iter()\r
377 .any(|r| r.matches(version) && r.pre_tag_is_compatible(version))\r
378 }\r
379\r
380 /// `is_exact()` returns `true` if there is exactly one version which could match this\r
381 /// `VersionReq`. If `false` is returned, it is possible that there may still only be exactly\r
382 /// one version which could match this `VersionReq`. This function is intended do allow\r
383 /// short-circuiting more complex logic where being able to handle only the possibility of a\r
384 /// single exact version may be cheaper.\r
385 ///\r
386 /// # Examples\r
387 ///\r
388 /// ```\r
389 /// use semver::ReqParseError;\r
390 /// use semver::VersionReq;\r
391 ///\r
392 /// fn use_is_exact() -> Result<(), ReqParseError> {\r
393 /// assert!(VersionReq::parse("=1.0.0")?.is_exact());\r
394 /// assert!(!VersionReq::parse("=1.0")?.is_exact());\r
395 /// assert!(!VersionReq::parse(">=1.0.0")?.is_exact());\r
396 /// Ok(())\r
397 /// }\r
398 ///\r
399 /// use_is_exact().unwrap();\r
400 /// ```\r
401 pub fn is_exact(&self) -> bool {\r
402 if let [range] = self.ranges.as_slice() {\r
403 if let [predicate] = range.predicates.as_slice() {\r
404 return predicate.has_exactly_one_match();\r
405 }\r
406 }\r
407\r
408 false\r
409 }\r
410}\r
411\r
412impl str::FromStr for VersionReq {\r
413 type Err = ReqParseError;\r
414\r
415 fn from_str(s: &str) -> Result<VersionReq, ReqParseError> {\r
416 VersionReq::parse(s)\r
417 }\r
418}\r
419\r
420impl Range {\r
421 fn matches(&self, ver: &Version) -> bool {\r
422 self.predicates.iter().all(|p| p.matches(ver))\r
423 }\r
424\r
425 fn pre_tag_is_compatible(&self, ver: &Version) -> bool {\r
426 self.predicates.iter().any(|p| p.pre_tag_is_compatible(ver))\r
427 }\r
428}\r
429\r
430impl Predicate {\r
431 fn exact(version: &Version) -> Predicate {\r
432 Predicate {\r
433 op: Ex,\r
434 major: version.major,\r
435 minor: version.minor,\r
436 patch: version.patch,\r
437 pre: version.pre.clone(),\r
438 }\r
439 }\r
440\r
441 /// `matches()` takes a `Version` and determines if it matches this particular `Predicate`.\r
442 pub fn matches(&self, ver: &Version) -> bool {\r
443 match self.op {\r
444 Ex => self.matches_exact(ver),\r
445 Gt => self.matches_greater(ver),\r
446 GtEq => self.matches_exact(ver) || self.matches_greater(ver),\r
447 Lt => !self.matches_exact(ver) && !self.matches_greater(ver),\r
448 LtEq => !self.matches_greater(ver),\r
449 }\r
450 }\r
451\r
452 fn matches_exact(&self, ver: &Version) -> bool {\r
453 self.major == ver.major\r
454 && self.minor == ver.minor\r
455 && self.patch == ver.patch\r
456 && self.pre == ver.pre\r
457 }\r
458\r
459 // https://docs.npmjs.com/misc/semver#prerelease-tags\r
460 fn pre_tag_is_compatible(&self, ver: &Version) -> bool {\r
461 // If a version has a prerelease tag (for example, 1.2.3-alpha.3) then it will\r
462 // only be\r
463 // allowed to satisfy comparator sets if at least one comparator with the same\r
464 // [major,\r
465 // minor, patch] tuple also has a prerelease tag.\r
466 !ver.is_prerelease()\r
467 || (self.major == ver.major\r
468 && self.minor == ver.minor\r
469 && self.patch == ver.patch\r
470 && !self.pre.is_empty())\r
471 }\r
472\r
473 fn matches_greater(&self, ver: &Version) -> bool {\r
474 if self.major != ver.major {\r
475 return ver.major > self.major;\r
476 }\r
477\r
478 if self.minor != ver.minor {\r
479 return ver.minor > self.minor;\r
480 }\r
481\r
482 if self.patch != ver.patch {\r
483 return ver.patch > self.patch;\r
484 }\r
485\r
486 if !self.pre.is_empty() {\r
487 return ver.pre.is_empty() || ver.pre > self.pre;\r
488 }\r
489\r
490 false\r
491 }\r
492\r
493 fn has_exactly_one_match(&self) -> bool {\r
494 self.op == Ex\r
495 }\r
496}\r
497\r
498impl fmt::Display for VersionReq {\r
499 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {\r
500 if self.ranges.is_empty() {\r
501 write!(fmt, "*")?;\r
502 } else {\r
503 for (i, ref pred) in self.ranges.iter().enumerate() {\r
504 if i == 0 {\r
505 write!(fmt, "{}", pred)?;\r
506 } else {\r
507 write!(fmt, " || {}", pred)?;\r
508 }\r
509 }\r
510 }\r
511\r
512 Ok(())\r
513 }\r
514}\r
515\r
516impl fmt::Display for Range {\r
517 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {\r
518 for (i, ref pred) in self.predicates.iter().enumerate() {\r
519 if i == 0 {\r
520 write!(fmt, "{}", pred)?;\r
521 } else if self.compat == Compat::Npm {\r
522 // Node does not expect commas between predicates\r
523 write!(fmt, " {}", pred)?;\r
524 } else {\r
525 write!(fmt, ", {}", pred)?;\r
526 }\r
527 }\r
528 Ok(())\r
529 }\r
530}\r
531\r
532impl fmt::Display for Predicate {\r
533 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {\r
534 write!(\r
535 fmt,\r
536 "{}{}.{}.{}",\r
537 self.op, self.major, self.minor, self.patch\r
538 )?;\r
539\r
540 if !self.pre.is_empty() {\r
541 write!(fmt, "-")?;\r
542 for (i, x) in self.pre.iter().enumerate() {\r
543 if i != 0 {\r
544 write!(fmt, ".")?\r
545 }\r
546 write!(fmt, "{}", x)?;\r
547 }\r
548 }\r
549\r
550 Ok(())\r
551 }\r
552}\r
553\r
554impl fmt::Display for Op {\r
555 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {\r
556 match *self {\r
557 Ex => write!(fmt, "=")?,\r
558 Gt => write!(fmt, ">")?,\r
559 GtEq => write!(fmt, ">=")?,\r
560 Lt => write!(fmt, "<")?,\r
561 LtEq => write!(fmt, "<=")?,\r
562 }\r
563 Ok(())\r
564 }\r
565}\r
566\r
567#[cfg(test)]\r
568mod test {\r
569 use super::super::version::Version;\r
570 use super::{Compat, Op, VersionReq};\r
571 use std::hash::{Hash, Hasher};\r
572\r
573 fn req(s: &str) -> VersionReq {\r
574 VersionReq::parse(s).unwrap()\r
575 }\r
576\r
577 fn req_npm(s: &str) -> VersionReq {\r
578 VersionReq::parse_compat(s, Compat::Npm).unwrap()\r
579 }\r
580\r
581 fn version(s: &str) -> Version {\r
582 match Version::parse(s) {\r
583 Ok(v) => v,\r
584 Err(e) => panic!("`{}` is not a valid version. Reason: {:?}", s, e),\r
585 }\r
586 }\r
587\r
588 fn assert_match(req: &VersionReq, vers: &[&str]) {\r
589 for ver in vers.iter() {\r
590 assert!(req.matches(&version(*ver)), "did not match {}", ver);\r
591 }\r
592 }\r
593\r
594 fn assert_not_match(req: &VersionReq, vers: &[&str]) {\r
595 for ver in vers.iter() {\r
596 assert!(!req.matches(&version(*ver)), "matched {}", ver);\r
597 }\r
598 }\r
599\r
600 fn calculate_hash<T: Hash>(t: T) -> u64 {\r
601 use std::collections::hash_map::DefaultHasher;\r
602\r
603 let mut s = DefaultHasher::new();\r
604 t.hash(&mut s);\r
605 s.finish()\r
606 }\r
607\r
608 #[test]\r
609 fn test_parsing_default() {\r
610 let r = req("1.0.0");\r
611\r
612 assert_eq!(r.to_string(), ">=1.0.0, <2.0.0".to_string());\r
613\r
614 assert_match(&r, &["1.0.0", "1.0.1"]);\r
615 assert_not_match(&r, &["0.9.9", "0.10.0", "0.1.0"]);\r
616 }\r
617\r
618 #[test]\r
619 fn test_parsing_default_npm() {\r
620 let r = req_npm("1.0.0");\r
621\r
622 assert_eq!(r.to_string(), "=1.0.0".to_string());\r
623\r
624 assert_match(&r, &["1.0.0"]);\r
625 assert_not_match(&r, &["0.9.9", "0.10.0", "0.1.0", "1.0.1"]);\r
626 }\r
627\r
628 #[test]\r
629 fn test_parsing_exact() {\r
630 let r = req("=1.0.0");\r
631\r
632 assert!(r.to_string() == "=1.0.0".to_string());\r
633 assert_eq!(r.to_string(), "=1.0.0".to_string());\r
634\r
635 assert_match(&r, &["1.0.0"]);\r
636 assert_not_match(&r, &["1.0.1", "0.9.9", "0.10.0", "0.1.0", "1.0.0-pre"]);\r
637\r
638 let r = req("=0.9.0");\r
639\r
640 assert_eq!(r.to_string(), "=0.9.0".to_string());\r
641\r
642 assert_match(&r, &["0.9.0"]);\r
643 assert_not_match(&r, &["0.9.1", "1.9.0", "0.0.9"]);\r
644\r
645 let r = req("=0.1.0-beta2.a");\r
646\r
647 assert_eq!(r.to_string(), "=0.1.0-beta2.a".to_string());\r
648\r
649 assert_match(&r, &["0.1.0-beta2.a"]);\r
650 assert_not_match(&r, &["0.9.1", "0.1.0", "0.1.1-beta2.a", "0.1.0-beta2"]);\r
651 }\r
652\r
653 #[test]\r
654 fn test_parse_metadata_see_issue_88_see_issue_88() {\r
655 for op in &[Op::Ex, Op::Gt, Op::GtEq, Op::Lt, Op::LtEq] {\r
656 println!("{} 1.2.3+meta", op);\r
657 req(&format!("{} 1.2.3+meta", op));\r
658 }\r
659 }\r
660\r
661 #[test]\r
662 pub fn test_parsing_greater_than() {\r
663 let r = req(">= 1.0.0");\r
664\r
665 assert_eq!(r.to_string(), ">=1.0.0".to_string());\r
666\r
667 assert_match(&r, &["1.0.0", "2.0.0"]);\r
668 assert_not_match(&r, &["0.1.0", "0.0.1", "1.0.0-pre", "2.0.0-pre"]);\r
669\r
670 // https://github.com/steveklabnik/semver/issues/53\r
671 let r = req(">= 2.1.0-alpha2");\r
672\r
673 assert_match(&r, &["2.1.0-alpha2", "2.1.0-alpha3", "2.1.0", "3.0.0"]);\r
674 assert_not_match(\r
675 &r,\r
676 &["2.0.0", "2.1.0-alpha1", "2.0.0-alpha2", "3.0.0-alpha2"],\r
677 );\r
678 }\r
679\r
680 #[test]\r
681 pub fn test_parsing_less_than() {\r
682 let r = req("< 1.0.0");\r
683\r
684 assert_eq!(r.to_string(), "<1.0.0".to_string());\r
685\r
686 assert_match(&r, &["0.1.0", "0.0.1"]);\r
687 assert_not_match(&r, &["1.0.0", "1.0.0-beta", "1.0.1", "0.9.9-alpha"]);\r
688\r
689 let r = req("<= 2.1.0-alpha2");\r
690\r
691 assert_match(&r, &["2.1.0-alpha2", "2.1.0-alpha1", "2.0.0", "1.0.0"]);\r
692 assert_not_match(\r
693 &r,\r
694 &["2.1.0", "2.2.0-alpha1", "2.0.0-alpha2", "1.0.0-alpha2"],\r
695 );\r
696 }\r
697\r
698 #[test]\r
699 pub fn test_multiple() {\r
700 let r = req("> 0.0.9, <= 2.5.3");\r
701 assert_eq!(r.to_string(), ">0.0.9, <=2.5.3".to_string());\r
702 assert_match(&r, &["0.0.10", "1.0.0", "2.5.3"]);\r
703 assert_not_match(&r, &["0.0.8", "2.5.4"]);\r
704\r
705 let r = req("0.3.0, 0.4.0");\r
706 assert_eq!(\r
707 r.to_string(),\r
708 ">=0.3.0, <0.4.0, >=0.4.0, <0.5.0".to_string()\r
709 );\r
710 assert_not_match(&r, &["0.0.8", "0.3.0", "0.4.0"]);\r
711\r
712 let r = req("<= 0.2.0, >= 0.5.0");\r
713 assert_eq!(r.to_string(), "<=0.2.0, >=0.5.0".to_string());\r
714 assert_not_match(&r, &["0.0.8", "0.3.0", "0.5.1"]);\r
715\r
716 let r = req("0.1.0, 0.1.4, 0.1.6");\r
717 assert_eq!(\r
718 r.to_string(),\r
719 ">=0.1.0, <0.2.0, >=0.1.4, <0.2.0, >=0.1.6, <0.2.0".to_string()\r
720 );\r
721 assert_match(&r, &["0.1.6", "0.1.9"]);\r
722 assert_not_match(&r, &["0.1.0", "0.1.4", "0.2.0"]);\r
723\r
724 assert!(VersionReq::parse("> 0.1.0,").is_err());\r
725 assert!(VersionReq::parse("> 0.3.0, ,").is_err());\r
726\r
727 let r = req(">=0.5.1-alpha3, <0.6");\r
728 assert_eq!(r.to_string(), ">=0.5.1-alpha3, <0.6.0".to_string());\r
729 assert_match(\r
730 &r,\r
731 &[\r
732 "0.5.1-alpha3",\r
733 "0.5.1-alpha4",\r
734 "0.5.1-beta",\r
735 "0.5.1",\r
736 "0.5.5",\r
737 ],\r
738 );\r
739 assert_not_match(\r
740 &r,\r
741 &["0.5.1-alpha1", "0.5.2-alpha3", "0.5.5-pre", "0.5.0-pre"],\r
742 );\r
743 assert_not_match(&r, &["0.6.0", "0.6.0-pre"]);\r
744\r
745 // https://github.com/steveklabnik/semver/issues/56\r
746 let r = req("1.2.3 - 2.3.4");\r
747 assert_eq!(r.to_string(), ">=1.2.3, <=2.3.4");\r
748 assert_match(&r, &["1.2.3", "1.2.10", "2.0.0", "2.3.4"]);\r
749 assert_not_match(&r, &["1.0.0", "1.2.2", "1.2.3-alpha1", "2.3.5"]);\r
750 }\r
751\r
752 // https://github.com/steveklabnik/semver/issues/55\r
753 #[test]\r
754 pub fn test_whitespace_delimited_comparator_sets() {\r
755 let r = req("> 0.0.9 <= 2.5.3");\r
756 assert_eq!(r.to_string(), ">0.0.9, <=2.5.3".to_string());\r
757 assert_match(&r, &["0.0.10", "1.0.0", "2.5.3"]);\r
758 assert_not_match(&r, &["0.0.8", "2.5.4"]);\r
759 }\r
760\r
761 #[test]\r
762 pub fn test_multiple_npm() {\r
763 let r = req_npm("> 0.0.9, <= 2.5.3");\r
764 assert_eq!(r.to_string(), ">0.0.9 <=2.5.3".to_string());\r
765 assert_match(&r, &["0.0.10", "1.0.0", "2.5.3"]);\r
766 assert_not_match(&r, &["0.0.8", "2.5.4"]);\r
767\r
768 let r = req_npm("0.3.0, 0.4.0");\r
769 assert_eq!(r.to_string(), "=0.3.0 =0.4.0".to_string());\r
770 assert_not_match(&r, &["0.0.8", "0.3.0", "0.4.0"]);\r
771\r
772 let r = req_npm("<= 0.2.0, >= 0.5.0");\r
773 assert_eq!(r.to_string(), "<=0.2.0 >=0.5.0".to_string());\r
774 assert_not_match(&r, &["0.0.8", "0.3.0", "0.5.1"]);\r
775\r
776 let r = req_npm("0.1.0, 0.1.4, 0.1.6");\r
777 assert_eq!(r.to_string(), "=0.1.0 =0.1.4 =0.1.6".to_string());\r
778 assert_not_match(&r, &["0.1.0", "0.1.4", "0.1.6", "0.2.0"]);\r
779\r
780 assert!(VersionReq::parse("> 0.1.0,").is_err());\r
781 assert!(VersionReq::parse("> 0.3.0, ,").is_err());\r
782\r
783 let r = req_npm(">=0.5.1-alpha3, <0.6");\r
784 assert_eq!(r.to_string(), ">=0.5.1-alpha3 <0.6.0".to_string());\r
785 assert_match(\r
786 &r,\r
787 &[\r
788 "0.5.1-alpha3",\r
789 "0.5.1-alpha4",\r
790 "0.5.1-beta",\r
791 "0.5.1",\r
792 "0.5.5",\r
793 ],\r
794 );\r
795 assert_not_match(\r
796 &r,\r
797 &["0.5.1-alpha1", "0.5.2-alpha3", "0.5.5-pre", "0.5.0-pre"],\r
798 );\r
799 assert_not_match(&r, &["0.6.0", "0.6.0-pre"]);\r
800 }\r
801\r
802 #[test]\r
803 pub fn test_parsing_tilde() {\r
804 let r = req("~1");\r
805 assert_match(&r, &["1.0.0", "1.0.1", "1.1.1"]);\r
806 assert_not_match(&r, &["0.9.1", "2.9.0", "0.0.9"]);\r
807\r
808 let r = req("~1.2");\r
809 assert_match(&r, &["1.2.0", "1.2.1"]);\r
810 assert_not_match(&r, &["1.1.1", "1.3.0", "0.0.9"]);\r
811\r
812 let r = req("~1.2.2");\r
813 assert_match(&r, &["1.2.2", "1.2.4"]);\r
814 assert_not_match(&r, &["1.2.1", "1.9.0", "1.0.9", "2.0.1", "0.1.3"]);\r
815\r
816 let r = req("~1.2.3-beta.2");\r
817 assert_match(&r, &["1.2.3", "1.2.4", "1.2.3-beta.2", "1.2.3-beta.4"]);\r
818 assert_not_match(&r, &["1.3.3", "1.1.4", "1.2.3-beta.1", "1.2.4-beta.2"]);\r
819 }\r
820\r
821 #[test]\r
822 pub fn test_parsing_compatible() {\r
823 let r = req("^1");\r
824 assert_match(&r, &["1.1.2", "1.1.0", "1.2.1", "1.0.1"]);\r
825 assert_not_match(&r, &["0.9.1", "2.9.0", "0.1.4"]);\r
826 assert_not_match(&r, &["1.0.0-beta1", "0.1.0-alpha", "1.0.1-pre"]);\r
827\r
828 let r = req("^1.1");\r
829 assert_match(&r, &["1.1.2", "1.1.0", "1.2.1"]);\r
830 assert_not_match(&r, &["0.9.1", "2.9.0", "1.0.1", "0.1.4"]);\r
831\r
832 let r = req("^1.1.2");\r
833 assert_match(&r, &["1.1.2", "1.1.4", "1.2.1"]);\r
834 assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1"]);\r
835 assert_not_match(&r, &["1.1.2-alpha1", "1.1.3-alpha1", "2.9.0-alpha1"]);\r
836\r
837 let r = req("^0.1.2");\r
838 assert_match(&r, &["0.1.2", "0.1.4"]);\r
839 assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1"]);\r
840 assert_not_match(&r, &["0.1.2-beta", "0.1.3-alpha", "0.2.0-pre"]);\r
841\r
842 let r = req("^0.5.1-alpha3");\r
843 assert_match(\r
844 &r,\r
845 &[\r
846 "0.5.1-alpha3",\r
847 "0.5.1-alpha4",\r
848 "0.5.1-beta",\r
849 "0.5.1",\r
850 "0.5.5",\r
851 ],\r
852 );\r
853 assert_not_match(\r
854 &r,\r
855 &[\r
856 "0.5.1-alpha1",\r
857 "0.5.2-alpha3",\r
858 "0.5.5-pre",\r
859 "0.5.0-pre",\r
860 "0.6.0",\r
861 ],\r
862 );\r
863\r
864 let r = req("^0.0.2");\r
865 assert_match(&r, &["0.0.2"]);\r
866 assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1", "0.1.4"]);\r
867\r
868 let r = req("^0.0");\r
869 assert_match(&r, &["0.0.2", "0.0.0"]);\r
870 assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.1.4"]);\r
871\r
872 let r = req("^0");\r
873 assert_match(&r, &["0.9.1", "0.0.2", "0.0.0"]);\r
874 assert_not_match(&r, &["2.9.0", "1.1.1"]);\r
875\r
876 let r = req("^1.4.2-beta.5");\r
877 assert_match(\r
878 &r,\r
879 &["1.4.2", "1.4.3", "1.4.2-beta.5", "1.4.2-beta.6", "1.4.2-c"],\r
880 );\r
881 assert_not_match(\r
882 &r,\r
883 &[\r
884 "0.9.9",\r
885 "2.0.0",\r
886 "1.4.2-alpha",\r
887 "1.4.2-beta.4",\r
888 "1.4.3-beta.5",\r
889 ],\r
890 );\r
891 }\r
892\r
893 #[test]\r
894 pub fn test_parsing_wildcard() {\r
895 let r = req("");\r
896 assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);\r
897 assert_not_match(&r, &[]);\r
898 let r = req("*");\r
899 assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);\r
900 assert_not_match(&r, &[]);\r
901 let r = req("x");\r
902 assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);\r
903 assert_not_match(&r, &[]);\r
904 let r = req("X");\r
905 assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]);\r
906 assert_not_match(&r, &[]);\r
907\r
908 let r = req("1.*");\r
909 assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]);\r
910 assert_not_match(&r, &["0.0.9"]);\r
911 let r = req("1.x");\r
912 assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]);\r
913 assert_not_match(&r, &["0.0.9"]);\r
914 let r = req("1.X");\r
915 assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]);\r
916 assert_not_match(&r, &["0.0.9"]);\r
917\r
918 let r = req("1.2.*");\r
919 assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]);\r
920 assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]);\r
921 let r = req("1.2.x");\r
922 assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]);\r
923 assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]);\r
924 let r = req("1.2.X");\r
925 assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]);\r
926 assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]);\r
927 }\r
928\r
929 // https://github.com/steveklabnik/semver/issues/57\r
930 #[test]\r
931 pub fn test_parsing_logical_or() {\r
932 let r = req("=1.2.3 || =2.3.4");\r
933 assert_eq!(r.to_string(), "=1.2.3 || =2.3.4".to_string());\r
934 assert_match(&r, &["1.2.3", "2.3.4"]);\r
935 assert_not_match(&r, &["1.0.0", "2.9.0", "0.1.4"]);\r
936 assert_not_match(&r, &["1.2.3-beta1", "2.3.4-alpha", "1.2.3-pre"]);\r
937\r
938 let r = req("1.1 || =1.2.3");\r
939 assert_eq!(r.to_string(), ">=1.1.0, <1.2.0 || =1.2.3".to_string());\r
940 assert_match(&r, &["1.1.0", "1.1.12", "1.2.3"]);\r
941 assert_not_match(&r, &["1.0.0", "1.2.2", "1.3.0"]);\r
942\r
943 let r = req("6.* || 8.* || >= 10.*");\r
944 assert_eq!(\r
945 r.to_string(),\r
946 ">=6.0.0, <7.0.0 || >=8.0.0, <9.0.0 || >=10.0.0".to_string()\r
947 );\r
948 assert_match(&r, &["6.0.0", "6.1.2"]);\r
949 assert_match(&r, &["8.0.0", "8.2.4"]);\r
950 assert_match(&r, &["10.1.2", "11.3.4"]);\r
951 assert_not_match(&r, &["5.0.0", "7.0.0", "9.0.0"]);\r
952 }\r
953\r
954 #[test]\r
955 pub fn test_parsing_logical_or_npm() {\r
956 let r = req_npm("=1.2.3 || =2.3.4");\r
957 assert_eq!(r.to_string(), "=1.2.3 || =2.3.4".to_string());\r
958 assert_match(&r, &["1.2.3", "2.3.4"]);\r
959 assert_not_match(&r, &["1.0.0", "2.9.0", "0.1.4"]);\r
960 assert_not_match(&r, &["1.2.3-beta1", "2.3.4-alpha", "1.2.3-pre"]);\r
961\r
962 let r = req_npm("1.1 || =1.2.3");\r
963 assert_eq!(r.to_string(), ">=1.1.0 <1.2.0 || =1.2.3".to_string());\r
964 assert_match(&r, &["1.1.0", "1.1.12", "1.2.3"]);\r
965 assert_not_match(&r, &["1.0.0", "1.2.2", "1.3.0"]);\r
966\r
967 let r = req_npm("6.* || 8.* || >= 10.*");\r
968 assert_eq!(\r
969 r.to_string(),\r
970 ">=6.0.0 <7.0.0 || >=8.0.0 <9.0.0 || >=10.0.0".to_string()\r
971 );\r
972 assert_match(&r, &["6.0.0", "6.1.2"]);\r
973 assert_match(&r, &["8.0.0", "8.2.4"]);\r
974 assert_match(&r, &["10.1.2", "11.3.4"]);\r
975 assert_not_match(&r, &["5.0.0", "7.0.0", "9.0.0"]);\r
976 }\r
977\r
978 #[test]\r
979 pub fn test_any() {\r
980 let r = VersionReq::any();\r
981 assert_match(&r, &["0.0.1", "0.1.0", "1.0.0"]);\r
982 }\r
983\r
984 #[test]\r
985 pub fn test_pre() {\r
986 let r = req("=2.1.1-really.0");\r
987 assert_match(&r, &["2.1.1-really.0"]);\r
988 }\r
989\r
990 // #[test]\r
991 // pub fn test_parse_errors() {\r
992 // assert_eq!(Err(InvalidVersionRequirement), VersionReq::parse("\0"));\r
993 // assert_eq!(Err(OpAlreadySet), VersionReq::parse(">= >= 0.0.2"));\r
994 // assert_eq!(Err(InvalidSigil), VersionReq::parse(">== 0.0.2"));\r
995 // assert_eq!(Err(VersionComponentsMustBeNumeric),\r
996 // VersionReq::parse("a.0.0"));\r
997 // assert_eq!(Err(InvalidIdentifier), VersionReq::parse("1.0.0-"));\r
998 // assert_eq!(Err(MajorVersionRequired), VersionReq::parse(">="));\r
999 // }\r
1000\r
1001 #[test]\r
1002 pub fn test_from_str() {\r
1003 assert_eq!(\r
1004 "1.0.0".parse::<VersionReq>().unwrap().to_string(),\r
1005 ">=1.0.0, <2.0.0".to_string()\r
1006 );\r
1007 assert_eq!(\r
1008 "=1.0.0".parse::<VersionReq>().unwrap().to_string(),\r
1009 "=1.0.0".to_string()\r
1010 );\r
1011 assert_eq!(\r
1012 "~1".parse::<VersionReq>().unwrap().to_string(),\r
1013 ">=1.0.0, <2.0.0".to_string()\r
1014 );\r
1015 assert_eq!(\r
1016 "~1.2".parse::<VersionReq>().unwrap().to_string(),\r
1017 ">=1.2.0, <1.3.0".to_string()\r
1018 );\r
1019 assert_eq!(\r
1020 "^1".parse::<VersionReq>().unwrap().to_string(),\r
1021 ">=1.0.0, <2.0.0".to_string()\r
1022 );\r
1023 assert_eq!(\r
1024 "^1.1".parse::<VersionReq>().unwrap().to_string(),\r
1025 ">=1.1.0, <2.0.0".to_string()\r
1026 );\r
1027 assert_eq!(\r
1028 "*".parse::<VersionReq>().unwrap().to_string(),\r
1029 ">=0.0.0".to_string()\r
1030 );\r
1031 assert_eq!(\r
1032 "1.*".parse::<VersionReq>().unwrap().to_string(),\r
1033 ">=1.0.0, <2.0.0".to_string()\r
1034 );\r
1035 assert_eq!(\r
1036 "< 1.0.0".parse::<VersionReq>().unwrap().to_string(),\r
1037 "<1.0.0".to_string()\r
1038 );\r
1039 }\r
1040\r
1041 // #[test]\r
1042 // pub fn test_from_str_errors() {\r
1043 // assert_eq!(Err(InvalidVersionRequirement), "\0".parse::<VersionReq>());\r
1044 // assert_eq!(Err(OpAlreadySet), ">= >= 0.0.2".parse::<VersionReq>());\r
1045 // assert_eq!(Err(InvalidSigil), ">== 0.0.2".parse::<VersionReq>());\r
1046 // assert_eq!(Err(VersionComponentsMustBeNumeric),\r
1047 // "a.0.0".parse::<VersionReq>());\r
1048 // assert_eq!(Err(InvalidIdentifier), "1.0.0-".parse::<VersionReq>());\r
1049 // assert_eq!(Err(MajorVersionRequired), ">=".parse::<VersionReq>());\r
1050 // }\r
1051\r
1052 #[test]\r
1053 fn test_cargo3202() {\r
1054 let v = "0.*.*".parse::<VersionReq>().unwrap();\r
1055 assert_eq!(">=0.0.0, <1.0.0", format!("{}", v.ranges[0]));\r
1056\r
1057 let v = "0.0.*".parse::<VersionReq>().unwrap();\r
1058 assert_eq!(">=0.0.0, <0.1.0", format!("{}", v.ranges[0]));\r
1059\r
1060 let r = req("0.*.*");\r
1061 assert_match(&r, &["0.5.0"]);\r
1062 }\r
1063\r
1064 #[test]\r
1065 fn test_eq_hash() {\r
1066 assert!(req("^1") == req("^1"));\r
1067 assert!(calculate_hash(req("^1")) == calculate_hash(req("^1")));\r
1068 assert!(req("^1") != req("^2"));\r
1069 }\r
1070\r
1071 #[test]\r
1072 fn test_ordering() {\r
1073 assert!(req("=1") > req("*"));\r
1074 assert!(req(">1") < req("*"));\r
1075 assert!(req(">=1") > req("*"));\r
1076 assert!(req("<1") > req("*"));\r
1077 assert!(req("<=1") > req("*"));\r
1078 assert!(req("~1") > req("*"));\r
1079 assert!(req("^1") > req("*"));\r
1080 assert!(req("*") == req("*"));\r
1081 }\r
1082\r
1083 #[test]\r
1084 fn is_exact() {\r
1085 assert!(req("=1.0.0").is_exact());\r
1086 assert!(req("=1.0.0-alpha").is_exact());\r
1087\r
1088 assert!(!req("=1").is_exact());\r
1089 assert!(!req(">=1.0.0").is_exact());\r
1090 assert!(!req(">=1.0.0, <2.0.0").is_exact());\r
1091 }\r
1092}\r