]> git.proxmox.com Git - rustc.git/blob - vendor/semver-parser/src/version.rs
New upstream version 1.59.0+dfsg1
[rustc.git] / vendor / semver-parser / src / version.rs
1 //! Version data and functions.
2 //!
3 //! This module contains [`Version`] struct, [`parse`] function for building
4 //! [`Version`] struct from string and some helper data structures and functions.
5 //!
6 //! # Examples
7 //!
8 //! Parsing `Version` from string and checking its fields:
9 //!
10 //! ```
11 //! use semver_parser::version;
12 //!
13 //! # fn try_main() -> Result<(), String> {
14 //! let version = version::parse("1.2.3-alpha1")?;
15 //!
16 //! assert_eq!(version.major, 1);
17 //! assert_eq!(version.minor, 2);
18 //! assert_eq!(version.patch, 3);
19 //!
20 //! let expected_pre = vec![
21 //! version::Identifier::AlphaNumeric(String::from("alpha1")),
22 //! ];
23 //!
24 //! assert_eq!(expected_pre, version.pre);
25 //! # Ok(())
26 //! # }
27 //! #
28 //! # try_main().unwrap();
29 //! ```
30 //! [`Version`]: ./struct.Version.html
31 //! [`parse`]: ./fn.parse.html
32
33 use crate::parser::{self, Parser};
34 use std::fmt;
35
36 /// Structure representing version data.
37 ///
38 /// `Version` struct has some public fields representing version data, like major/minor version
39 /// string, patch number and vectors of prefix and build identifiers.
40 ///
41 /// # Examples
42 ///
43 /// Parsing `Version` from string and checking its fields:
44 ///
45 /// ```
46 /// use semver_parser::version;
47 ///
48 /// # fn try_main() -> Result<(), String> {
49 /// let version = version::parse("0.1.2-alpha1")?;
50 /// assert_eq!(version.major, 0);
51 /// assert_eq!(version.minor, 1);
52 /// assert_eq!(version.patch, 2);
53 /// let expected_pre = vec![version::Identifier::AlphaNumeric(String::from("alpha1"))];
54 /// assert_eq!(expected_pre, version.pre);
55 /// # Ok(())
56 /// # }
57 /// #
58 /// # try_main().unwrap();
59 /// ```
60 #[derive(Clone, PartialOrd, Ord, Hash, Debug, PartialEq, Eq)]
61 pub struct Version {
62 /// Major version as number (`0` in `"0.1.2"`).
63 pub major: u64,
64 /// Minor version as number (`1` in `"0.1.2"`).
65 pub minor: u64,
66 /// Patch version as number (`2` in `"0.1.2"`).
67 pub patch: u64,
68 /// Pre-release metadata as a vector of `Identifier` (`"alpha1"` in `"0.1.2-alpha1"`
69 /// or `7` (numeric) in `"0.1.2-7"`, `"pre"` and `0` (numeric) in `"0.1.2-pre.0"`).
70 pub pre: Vec<Identifier>,
71 /// Build metadata as a vector of `Identifier` (`"build1"` in `"0.1.2+build1"`
72 /// or `7` (numeric) in `"0.1.2+7"`, `"build"` and `0` (numeric) in `"0.1.2+pre.0"`).
73 pub build: Vec<Identifier>,
74 }
75
76 /// Helper enum for holding data of alphanumeric or numeric suffix identifiers.
77 ///
78 /// This enum is used to hold suffix parts of `pre` and `build` fields of
79 /// [`Version`] struct. Theses suffixes may be either numeric or alphanumeric.
80 ///
81 /// # Examples
82 ///
83 /// Parsing [`Version`] with pre-release part composed of two `Identifier`s:
84 ///
85 /// ```
86 /// use semver_parser::version;
87 ///
88 /// # fn try_main() -> Result<(), String> {
89 /// let version = version::parse("0.1.2-alpha1.0")?;
90 ///
91 /// let expected_pre = vec![
92 /// version::Identifier::AlphaNumeric(String::from("alpha1")),
93 /// version::Identifier::Numeric(0),
94 /// ];
95 ///
96 /// assert_eq!(expected_pre, version.pre);
97 /// # Ok(())
98 /// # }
99 /// #
100 /// # try_main().unwrap();
101 /// ```
102 /// [`Version`]: ./struct.Version.html
103 #[derive(Clone, PartialOrd, Ord, Hash, Debug, PartialEq, Eq)]
104 pub enum Identifier {
105 /// An identifier that's solely numbers.
106 Numeric(u64),
107 /// An identifier with letters and numbers.
108 AlphaNumeric(String),
109 }
110
111 impl Identifier {
112 pub fn concat(self, add_str: &str) -> Identifier {
113 match self {
114 Identifier::Numeric(n) => Identifier::AlphaNumeric(format!("{}{}", n, add_str)),
115 Identifier::AlphaNumeric(s) => Identifier::AlphaNumeric(format!("{}{}", s, add_str)),
116 }
117 }
118 }
119
120 /// Function for parsing version string to [`Version`].
121 ///
122 /// Returns `Result<`[`Version`]`, String>`, where `String` represents an error while parsing.
123 ///
124 /// # Examples
125 ///
126 /// Parsing [`Version`] from string and checking its fields:
127 ///
128 /// ```
129 /// use semver_parser::version;
130 ///
131 /// # fn try_main() -> Result<(), String> {
132 /// let version = version::parse("0.1.2-alpha1")?;
133 /// assert_eq!(version.major, 0);
134 /// assert_eq!(version.minor, 1);
135 /// assert_eq!(version.patch, 2);
136 /// let expected_pre = vec![version::Identifier::AlphaNumeric(String::from("alpha1"))];
137 /// assert_eq!(expected_pre, version.pre);
138 /// # Ok(())
139 /// # }
140 /// #
141 /// # try_main().unwrap();
142 /// ```
143 /// [`Version`]: ./struct.Version.html
144 pub fn parse(input: &str) -> Result<Version, parser::Error> {
145 let mut parser = Parser::new(input)?;
146 let version = parser.version()?;
147
148 if !parser.is_eof() {
149 return Err(parser::Error::MoreInput(parser.tail()?));
150 }
151
152 Ok(version)
153 }
154
155 impl fmt::Display for Version {
156 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157 write!(f, "{}.{}.{}", self.major, self.minor, self.patch).expect("write failed");
158 if !self.pre.is_empty() {
159 let strs: Vec<_> = self.pre.iter().map(ToString::to_string).collect();
160 write!(f, "-{}", strs.join(".")).expect("write failed");
161 }
162 if !self.build.is_empty() {
163 let strs: Vec<_> = self.build.iter().map(ToString::to_string).collect();
164 write!(f, "+{}", strs.join(".")).expect("write failed");
165 }
166 Ok(())
167 }
168 }
169
170 impl fmt::Display for Identifier {
171 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
172 match *self {
173 Identifier::Numeric(ref id) => id.fmt(f),
174 Identifier::AlphaNumeric(ref id) => id.fmt(f),
175 }
176 }
177 }
178
179 #[cfg(test)]
180 mod tests {
181 use super::*;
182 use crate::version;
183
184 #[test]
185 fn parse_empty() {
186 let version = "";
187
188 let parsed = version::parse(version);
189
190 assert!(
191 parsed.is_err(),
192 "empty string incorrectly considered a valid parse"
193 );
194 }
195
196 #[test]
197 fn parse_blank() {
198 let version = " ";
199
200 let parsed = version::parse(version);
201
202 assert!(
203 parsed.is_err(),
204 "blank string incorrectly considered a valid parse"
205 );
206 }
207
208 #[test]
209 fn parse_no_minor_patch() {
210 let version = "1";
211
212 let parsed = version::parse(version);
213
214 assert!(
215 parsed.is_err(),
216 format!("'{}' incorrectly considered a valid parse", version)
217 );
218 }
219
220 #[test]
221 fn parse_no_patch() {
222 let version = "1.2";
223
224 let parsed = version::parse(version);
225
226 assert!(
227 parsed.is_err(),
228 format!("'{}' incorrectly considered a valid parse", version)
229 );
230 }
231
232 #[test]
233 fn parse_empty_pre() {
234 let version = "1.2.3-";
235
236 let parsed = version::parse(version);
237
238 assert!(
239 parsed.is_err(),
240 format!("'{}' incorrectly considered a valid parse", version)
241 );
242 }
243
244 #[test]
245 fn parse_letters() {
246 let version = "a.b.c";
247
248 let parsed = version::parse(version);
249
250 assert!(
251 parsed.is_err(),
252 format!("'{}' incorrectly considered a valid parse", version)
253 );
254 }
255
256 #[test]
257 fn parse_with_letters() {
258 let version = "1.2.3 a.b.c";
259
260 let parsed = version::parse(version);
261
262 assert!(
263 parsed.is_err(),
264 format!("'{}' incorrectly considered a valid parse", version)
265 );
266 }
267
268 #[test]
269 fn parse_basic_version() {
270 let version = "1.2.3";
271
272 let parsed = version::parse(version).unwrap();
273
274 assert_eq!(1, parsed.major);
275 assert_eq!(2, parsed.minor);
276 assert_eq!(3, parsed.patch);
277 }
278
279 #[test]
280 fn parse_trims_input() {
281 let version = " 1.2.3 ";
282
283 let parsed = version::parse(version).unwrap();
284
285 assert_eq!(1, parsed.major);
286 assert_eq!(2, parsed.minor);
287 assert_eq!(3, parsed.patch);
288 }
289
290 #[test]
291 fn parse_no_major_leading_zeroes() {
292 let version = "01.0.0";
293
294 let parsed = version::parse(version);
295
296 assert!(
297 parsed.is_err(),
298 "01 incorrectly considered a valid major version"
299 );
300 }
301
302 #[test]
303 fn parse_no_minor_leading_zeroes() {
304 let version = "0.01.0";
305
306 let parsed = version::parse(version);
307
308 assert!(
309 parsed.is_err(),
310 "01 incorrectly considered a valid minor version"
311 );
312 }
313
314 #[test]
315 fn parse_no_patch_leading_zeroes() {
316 let version = "0.0.01";
317
318 let parsed = version::parse(version);
319
320 assert!(
321 parsed.is_err(),
322 "01 incorrectly considered a valid patch version"
323 );
324 }
325
326 #[test]
327 fn parse_no_major_overflow() {
328 let version = "98765432109876543210.0.0";
329
330 let parsed = version::parse(version);
331
332 assert!(
333 parsed.is_err(),
334 "98765432109876543210 incorrectly considered a valid major version"
335 );
336 }
337
338 #[test]
339 fn parse_no_minor_overflow() {
340 let version = "0.98765432109876543210.0";
341
342 let parsed = version::parse(version);
343
344 assert!(
345 parsed.is_err(),
346 "98765432109876543210 incorrectly considered a valid minor version"
347 );
348 }
349
350 #[test]
351 fn parse_no_patch_overflow() {
352 let version = "0.0.98765432109876543210";
353
354 let parsed = version::parse(version);
355
356 assert!(
357 parsed.is_err(),
358 "98765432109876543210 incorrectly considered a valid patch version"
359 );
360 }
361
362 #[test]
363 fn parse_basic_prerelease() {
364 let version = "1.2.3-pre";
365
366 let parsed = version::parse(version).unwrap();
367
368 let expected_pre = vec![Identifier::AlphaNumeric(String::from("pre"))];
369 assert_eq!(expected_pre, parsed.pre);
370 }
371
372 #[test]
373 fn parse_prerelease_alphanumeric() {
374 let version = "1.2.3-alpha1";
375
376 let parsed = version::parse(version).unwrap();
377
378 let expected_pre = vec![Identifier::AlphaNumeric(String::from("alpha1"))];
379 assert_eq!(expected_pre, parsed.pre);
380 }
381
382 #[test]
383 fn parse_prerelease_zero() {
384 let version = "1.2.3-pre.0";
385
386 let parsed = version::parse(version).unwrap();
387
388 let expected_pre = vec![
389 Identifier::AlphaNumeric(String::from("pre")),
390 Identifier::Numeric(0),
391 ];
392 assert_eq!(expected_pre, parsed.pre);
393 }
394
395 #[test]
396 fn parse_basic_build() {
397 let version = "1.2.3+build";
398
399 let parsed = version::parse(version).unwrap();
400
401 let expected_build = vec![Identifier::AlphaNumeric(String::from("build"))];
402 assert_eq!(expected_build, parsed.build);
403 }
404
405 #[test]
406 fn parse_build_alphanumeric() {
407 let version = "1.2.3+build5";
408
409 let parsed = version::parse(version).unwrap();
410
411 let expected_build = vec![Identifier::AlphaNumeric(String::from("build5"))];
412 assert_eq!(expected_build, parsed.build);
413 }
414
415 #[test]
416 fn parse_pre_and_build() {
417 let version = "1.2.3-alpha1+build5";
418
419 let parsed = version::parse(version).unwrap();
420
421 let expected_pre = vec![Identifier::AlphaNumeric(String::from("alpha1"))];
422 assert_eq!(expected_pre, parsed.pre);
423
424 let expected_build = vec![Identifier::AlphaNumeric(String::from("build5"))];
425 assert_eq!(expected_build, parsed.build);
426 }
427
428 #[test]
429 fn parse_complex_metadata_01() {
430 let version = "1.2.3-1.alpha1.9+build5.7.3aedf ";
431
432 let parsed = version::parse(version).unwrap();
433
434 let expected_pre = vec![
435 Identifier::Numeric(1),
436 Identifier::AlphaNumeric(String::from("alpha1")),
437 Identifier::Numeric(9),
438 ];
439 assert_eq!(expected_pre, parsed.pre);
440
441 let expected_build = vec![
442 Identifier::AlphaNumeric(String::from("build5")),
443 Identifier::Numeric(7),
444 Identifier::AlphaNumeric(String::from("3aedf")),
445 ];
446 assert_eq!(expected_build, parsed.build);
447 }
448
449 #[test]
450 fn parse_complex_metadata_02() {
451 let version = "0.4.0-beta.1+0851523";
452
453 let parsed = version::parse(version).unwrap();
454
455 let expected_pre = vec![
456 Identifier::AlphaNumeric(String::from("beta")),
457 Identifier::Numeric(1),
458 ];
459 assert_eq!(expected_pre, parsed.pre);
460
461 let expected_build = vec![Identifier::AlphaNumeric(String::from("0851523"))];
462 assert_eq!(expected_build, parsed.build);
463 }
464
465 #[test]
466 fn parse_metadata_overflow() {
467 let version = "0.4.0-beta.1+98765432109876543210";
468
469 let parsed = version::parse(version).unwrap();
470
471 let expected_pre = vec![
472 Identifier::AlphaNumeric(String::from("beta")),
473 Identifier::Numeric(1),
474 ];
475 assert_eq!(expected_pre, parsed.pre);
476
477 let expected_build = vec![Identifier::AlphaNumeric(String::from(
478 "98765432109876543210",
479 ))];
480 assert_eq!(expected_build, parsed.build);
481 }
482
483 #[test]
484 fn parse_regression_01() {
485 let version = "0.0.0-WIP";
486
487 let parsed = version::parse(version).unwrap();
488
489 assert_eq!(0, parsed.major);
490 assert_eq!(0, parsed.minor);
491 assert_eq!(0, parsed.patch);
492
493 let expected_pre = vec![Identifier::AlphaNumeric(String::from("WIP"))];
494 assert_eq!(expected_pre, parsed.pre);
495 }
496
497 #[test]
498 fn parse_regression_02() {
499 // this is used by really old versions of npm, and is valid according to semver.org
500 let version = "1.2.3-beta-1";
501
502 let parsed = version::parse(version).unwrap();
503
504 assert_eq!(1, parsed.major);
505 assert_eq!(2, parsed.minor);
506 assert_eq!(3, parsed.patch);
507
508 let expected_pre = vec![Identifier::AlphaNumeric(String::from("beta-1"))];
509 assert_eq!(expected_pre, parsed.pre);
510 }
511 }