]>
Commit | Line | Data |
---|---|---|
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 | |
11 | use std::error::Error;\r | |
12 | use std::fmt;\r | |
13 | use std::str;\r | |
14 | \r | |
15 | use semver_parser;\r | |
16 | use semver_parser::{Compat, RangeSet};\r | |
17 | use version::Identifier;\r | |
18 | use Version;\r | |
19 | \r | |
20 | #[cfg(feature = "serde")]\r | |
21 | use serde::de::{self, Deserialize, Deserializer, Visitor};\r | |
22 | #[cfg(feature = "serde")]\r | |
23 | use serde::ser::{Serialize, Serializer};\r | |
24 | \r | |
25 | use self::Op::{Ex, Gt, GtEq, Lt, LtEq};\r | |
26 | use 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 | |
34 | pub struct VersionReq {\r | |
35 | ranges: Vec<Range>,\r | |
36 | compat: Compat, // defaults to Cargo\r | |
37 | }\r | |
38 | \r | |
39 | impl 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 | |
49 | impl 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 | |
61 | impl<'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 | |
89 | enum 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 | |
97 | impl 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 | |
110 | struct Range {\r | |
111 | predicates: Vec<Predicate>,\r | |
112 | compat: Compat,\r | |
113 | }\r | |
114 | \r | |
115 | impl 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 | |
125 | struct 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 | |
133 | impl 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 | |
145 | impl 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 | |
158 | pub 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 | |
177 | impl 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 | |
197 | impl Error for ReqParseError {}\r | |
198 | \r | |
199 | impl 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 | |
209 | impl 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 | |
412 | impl 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 | |
420 | impl 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 | |
430 | impl 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 | |
498 | impl 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 | |
516 | impl 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 | |
532 | impl 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 | |
554 | impl 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 | |
568 | mod 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 |