node_semver_with_ord/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::cmp::{self, Ordering};
4use std::fmt;
5use std::num::ParseIntError;
6
7use miette::{Diagnostic, SourceSpan};
8use serde::{
9    de::{self, Deserialize, Deserializer, Visitor},
10    ser::{Serialize, Serializer},
11};
12use thiserror::Error;
13
14use nom::branch::alt;
15use nom::bytes::complete::{tag, take_while1};
16use nom::character::complete::{digit1, space0};
17use nom::character::is_alphanumeric;
18use nom::combinator::{all_consuming, map, map_res, opt, recognize};
19use nom::error::{context, ContextError, ErrorKind, FromExternalError, ParseError};
20use nom::multi::separated_list1;
21use nom::sequence::{preceded, tuple};
22use nom::{Err, IResult};
23
24pub use range::*;
25
26mod range;
27
28/// JavaScript's
29/// [MAX_SAFE_INTEGER](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER).
30/// This is used to determine the maximum value for integer components in a
31/// JS-compatible way.
32pub const MAX_SAFE_INTEGER: u64 = 900_719_925_474_099;
33
34/// Maximum length of a semver string.
35pub const MAX_LENGTH: usize = 256;
36
37/**
38Semver version or range parsing error wrapper.
39
40This wrapper is used to hold some parsing-related metadata, as well as
41a more specific [SemverErrorKind].
42*/
43#[derive(Debug, Clone, Error, Eq, PartialEq)]
44#[error("{kind}")]
45pub struct SemverError {
46    input: String,
47    span: SourceSpan,
48    kind: SemverErrorKind,
49}
50
51impl Diagnostic for SemverError {
52    fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
53        self.kind().code()
54    }
55
56    fn severity(&self) -> Option<miette::Severity> {
57        self.kind().severity()
58    }
59
60    fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
61        self.kind().help()
62    }
63
64    fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
65        self.kind().url()
66    }
67
68    fn source_code(&self) -> Option<&dyn miette::SourceCode> {
69        Some(&self.input)
70    }
71
72    fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
73        Some(Box::new(std::iter::once(
74            miette::LabeledSpan::new_with_span(Some("here".into()), self.span().clone()),
75        )))
76    }
77}
78
79impl SemverError {
80    /// Returns the input that was given to the parser.
81    pub fn input(&self) -> &str {
82        &self.input
83    }
84
85    /// Returns the SourceSpan of the error.
86    pub fn span(&self) -> &SourceSpan {
87        &self.span
88    }
89
90    /// Returns the (0-based) byte offset where the parsing error happened.
91    pub fn offset(&self) -> usize {
92        self.span.offset()
93    }
94
95    /// Returns the more specific [SemverErrorKind] for this error.
96    ///
97    /// This value can also be fetched through [std::error::Error::source],
98    /// but that would require downcasting to match types.
99    pub fn kind(&self) -> &SemverErrorKind {
100        &self.kind
101    }
102
103    /// Returns the (0-indexed) line and column number where the parsing error
104    /// happened.
105    pub fn location(&self) -> (usize, usize) {
106        // Taken partially from nom.
107        let prefix = &self.input.as_bytes()[..self.offset()];
108
109        // Count the number of newlines in the first `offset` bytes of input
110        let line_number = bytecount::count(prefix, b'\n');
111
112        // Find the line that includes the subslice:
113        // Find the *last* newline before the substring starts
114        let line_begin = prefix
115            .iter()
116            .rev()
117            .position(|&b| b == b'\n')
118            .map(|pos| self.offset() - pos)
119            .unwrap_or(0);
120
121        // Find the full line after that newline
122        let line = self.input[line_begin..]
123            .lines()
124            .next()
125            .unwrap_or(&self.input[line_begin..])
126            .trim_end();
127
128        // The (0-indexed) column number is the offset of our substring into that line
129        let column_number = self.input[self.offset()..].as_ptr() as usize - line.as_ptr() as usize;
130
131        (line_number, column_number)
132    }
133}
134
135/**
136The specific kind of error that occurred. Usually wrapped in a [SemverError].
137*/
138#[derive(Debug, Clone, Error, Eq, PartialEq, Diagnostic)]
139pub enum SemverErrorKind {
140    /**
141    Semver strings overall can't be longer than [MAX_LENGTH]. This is a
142    restriction coming from the JavaScript `node-semver`.
143    */
144    #[error("Semver string can't be longer than {} characters.", MAX_LENGTH)]
145    #[diagnostic(code(node_semver::too_long), url(docsrs))]
146    MaxLengthError,
147
148    /**
149    Input to `node-semver` must be "complete". That is, a version must be
150    composed of major, minor, and patch segments, with optional prerelease
151    and build metadata. If you're looking for alternative syntaxes, like `1.2`,
152    that are meant for defining semver ranges, use [Range] instead.
153    */
154    #[error("Incomplete input to semver parser.")]
155    #[diagnostic(code(node_semver::incomplete_input), url(docsrs))]
156    IncompleteInput,
157
158    /**
159    Components of a semver string (major, minor, patch, integer sections of
160    build and prerelease) must all be valid, parseable integers. This error
161    occurs when Rust's own integer parsing failed.
162    */
163    #[error("Failed to parse an integer component of a semver string: {0}")]
164    #[diagnostic(code(node_semver::parse_int_error), url(docsrs))]
165    ParseIntError(ParseIntError),
166
167    /**
168    `node-semver` inherits the JavaScript implementation's limitation on
169    limiting integer component sizes to [MAX_SAFE_INTEGER].
170    */
171    #[error("Integer component of semver string is larger than JavaScript's Number.MAX_SAFE_INTEGER: {0}")]
172    #[diagnostic(code(node_semver::integer_too_large), url(docsrs))]
173    MaxIntError(u64),
174
175    /**
176    This is a generic error that a certain component of the semver string
177    failed to parse.
178    */
179    #[error("Failed to parse {0}.")]
180    #[diagnostic(code(node_semver::parse_component_error), url(docsrs))]
181    Context(&'static str),
182
183    /**
184     */
185    #[error("No valid ranges could be parsed")]
186    #[diagnostic(code(node_semver::no_valid_ranges), url(docsrs), help("node-semver parses in so-called 'loose' mode. This means that if you have a slightly incorrect semver operator (`>=1.y`, for ex.), it will get thrown away. This error only happens if _all_ your input ranges were invalid semver in this way."))]
187    NoValidRanges,
188
189    /**
190    This error is mostly nondescript. Feel free to file an issue if you run
191    into it.
192    */
193    #[error("An unspecified error occurred.")]
194    #[diagnostic(code(node_semver::other), url(docsrs))]
195    Other,
196}
197
198#[derive(Debug)]
199struct SemverParseError<I> {
200    pub(crate) input: I,
201    pub(crate) context: Option<&'static str>,
202    pub(crate) kind: Option<SemverErrorKind>,
203}
204
205impl<I> ParseError<I> for SemverParseError<I> {
206    fn from_error_kind(input: I, _kind: nom::error::ErrorKind) -> Self {
207        Self {
208            input,
209            context: None,
210            kind: None,
211        }
212    }
213
214    fn append(_input: I, _kind: nom::error::ErrorKind, other: Self) -> Self {
215        other
216    }
217}
218
219impl<I> ContextError<I> for SemverParseError<I> {
220    fn add_context(_input: I, ctx: &'static str, mut other: Self) -> Self {
221        other.context = Some(ctx);
222        other
223    }
224}
225
226impl<'a> FromExternalError<&'a str, SemverParseError<&'a str>> for SemverParseError<&'a str> {
227    fn from_external_error(
228        _input: &'a str,
229        _kind: ErrorKind,
230        e: SemverParseError<&'a str>,
231    ) -> Self {
232        e
233    }
234}
235
236/**
237An Identifier type for build and prerelease metadata.
238*/
239#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
240pub enum Identifier {
241    /// An identifier that's solely numbers.
242    Numeric(u64),
243    /// An identifier with letters and numbers.
244    AlphaNumeric(String),
245}
246
247impl fmt::Display for Identifier {
248    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249        match self {
250            Identifier::Numeric(n) => write!(f, "{}", n),
251            Identifier::AlphaNumeric(s) => write!(f, "{}", s),
252        }
253    }
254}
255
256/**
257A semantic version, conformant to the [semver spec](https://semver.org/spec/v2.0.0.html).
258*/
259#[derive(Clone, Debug)]
260pub struct Version {
261    pub major: u64,
262    pub minor: u64,
263    pub patch: u64,
264    pub build: Vec<Identifier>,
265    pub pre_release: Vec<Identifier>,
266}
267
268impl Version {
269    /// True if this [Version] satisfies the given [Range].
270    pub fn satisfies(&self, range: &Range) -> bool {
271        range.satisfies(self)
272    }
273
274    /// True is this [Version] has a prerelease component.
275    pub fn is_prerelease(&self) -> bool {
276        !self.pre_release.is_empty()
277    }
278
279    /// Parse a semver string into a [Version].
280    pub fn parse<S: AsRef<str>>(input: S) -> Result<Version, SemverError> {
281        let input = input.as_ref();
282
283        if input.len() > MAX_LENGTH {
284            return Err(SemverError {
285                input: input.into(),
286                span: (input.len() - 1, 0).into(),
287                kind: SemverErrorKind::MaxLengthError,
288            });
289        }
290
291        match all_consuming(version)(input) {
292            Ok((_, arg)) => Ok(arg),
293            Err(err) => Err(match err {
294                Err::Error(e) | Err::Failure(e) => SemverError {
295                    input: input.into(),
296                    span: (e.input.as_ptr() as usize - input.as_ptr() as usize, 0).into(),
297                    kind: if let Some(kind) = e.kind {
298                        kind
299                    } else if let Some(ctx) = e.context {
300                        SemverErrorKind::Context(ctx)
301                    } else {
302                        SemverErrorKind::Other
303                    },
304                },
305                Err::Incomplete(_) => SemverError {
306                    input: input.into(),
307                    span: (input.len() - 1, 0).into(),
308                    kind: SemverErrorKind::IncompleteInput,
309                },
310            }),
311        }
312    }
313}
314
315impl PartialEq for Version {
316    fn eq(&self, other: &Self) -> bool {
317        self.major == other.major
318            && self.minor == other.minor
319            && self.patch == other.patch
320            && self.pre_release == other.pre_release
321    }
322}
323
324impl Eq for Version {}
325
326impl std::hash::Hash for Version {
327    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
328        self.major.hash(state);
329        self.minor.hash(state);
330        self.patch.hash(state);
331        self.pre_release.hash(state);
332    }
333}
334
335impl Serialize for Version {
336    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
337    where
338        S: Serializer,
339    {
340        serializer.collect_str(self)
341    }
342}
343
344impl<'de> Deserialize<'de> for Version {
345    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
346    where
347        D: Deserializer<'de>,
348    {
349        struct IntegrityVisitor;
350
351        impl<'de> Visitor<'de> for IntegrityVisitor {
352            type Value = Version;
353
354            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
355                formatter.write_str("a version string")
356            }
357
358            fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
359            where
360                E: de::Error,
361            {
362                Version::parse(v).map_err(de::Error::custom)
363            }
364        }
365
366        deserializer.deserialize_str(IntegrityVisitor)
367    }
368}
369
370impl fmt::Display for Version {
371    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?;
373
374        for (i, ident) in self.pre_release.iter().enumerate() {
375            if i == 0 {
376                write!(f, "-")?;
377            } else {
378                write!(f, ".")?;
379            }
380            write!(f, "{}", ident)?;
381        }
382
383        for (i, ident) in self.build.iter().enumerate() {
384            if i == 0 {
385                write!(f, "+")?;
386            } else {
387                write!(f, ".")?;
388            }
389            write!(f, "{}", ident)?;
390        }
391
392        Ok(())
393    }
394}
395
396impl std::convert::From<(u64, u64, u64)> for Version {
397    fn from((major, minor, patch): (u64, u64, u64)) -> Self {
398        Version {
399            major,
400            minor,
401            patch,
402            build: Vec::new(),
403            pre_release: Vec::new(),
404        }
405    }
406}
407
408impl std::str::FromStr for Version {
409    type Err = SemverError;
410    fn from_str(s: &str) -> Result<Self, Self::Err> {
411        Version::parse(s)
412    }
413}
414
415impl std::convert::From<(u64, u64, u64, u64)> for Version {
416    fn from((major, minor, patch, pre_release): (u64, u64, u64, u64)) -> Self {
417        Version {
418            major,
419            minor,
420            patch,
421            build: Vec::new(),
422            pre_release: vec![Identifier::Numeric(pre_release)],
423        }
424    }
425}
426
427impl cmp::PartialOrd for Version {
428    fn partial_cmp(&self, other: &Version) -> Option<Ordering> {
429        Some(self.cmp(other))
430    }
431}
432
433impl cmp::Ord for Version {
434    fn cmp(&self, other: &Version) -> cmp::Ordering {
435        match self.major.cmp(&other.major) {
436            Ordering::Equal => {}
437            //if difference in major version, just return result
438            order_result => return order_result,
439        }
440
441        match self.minor.cmp(&other.minor) {
442            Ordering::Equal => {}
443            //if difference in minor version, just return result
444            order_result => return order_result,
445        }
446
447        match self.patch.cmp(&other.patch) {
448            Ordering::Equal => {}
449            //if difference in patch version, just return result
450            order_result => return order_result,
451        }
452
453        match (self.pre_release.len(), other.pre_release.len()) {
454            //if no pre_release string, they're equal
455            (0, 0) => Ordering::Equal,
456            //if other has a pre-release string, but this doesn't, this one is greater
457            (0, _) => Ordering::Greater,
458            //if this one has a pre-release string, but other doesn't this one is less than
459            (_, 0) => Ordering::Less,
460            // if both have pre_release strings, compare the strings and return the result
461            (_, _) => self.pre_release.cmp(&other.pre_release),
462        }
463    }
464}
465
466enum Extras {
467    Build(Vec<Identifier>),
468    Release(Vec<Identifier>),
469    ReleaseAndBuild((Vec<Identifier>, Vec<Identifier>)),
470}
471
472impl Extras {
473    fn values(self) -> (Vec<Identifier>, Vec<Identifier>) {
474        use Extras::*;
475        match self {
476            Release(ident) => (ident, Vec::new()),
477            Build(ident) => (Vec::new(), ident),
478            ReleaseAndBuild(ident) => ident,
479        }
480    }
481}
482
483/// <valid semver> ::= <version core>
484///                 | <version core> "-" <pre-release>
485///                 | <version core> "+" <build>
486///                 | <version core> "-" <pre-release> "+" <build>
487fn version(input: &str) -> IResult<&str, Version, SemverParseError<&str>> {
488    context(
489        "version",
490        map(
491            tuple((opt(alt((tag("v"), tag("V")))), space0, version_core, extras)),
492            |(_, _, (major, minor, patch), (pre_release, build))| Version {
493                major,
494                minor,
495                patch,
496                pre_release,
497                build,
498            },
499        ),
500    )(input)
501}
502
503fn extras(
504    input: &str,
505) -> IResult<&str, (Vec<Identifier>, Vec<Identifier>), SemverParseError<&str>> {
506    map(
507        opt(alt((
508            map(tuple((pre_release, build)), Extras::ReleaseAndBuild),
509            map(pre_release, Extras::Release),
510            map(build, Extras::Build),
511        ))),
512        |extras| match extras {
513            Some(extras) => extras.values(),
514            _ => Default::default(),
515        },
516    )(input)
517}
518
519/// <version core> ::= <major> "." <minor> "." <patch>
520fn version_core(input: &str) -> IResult<&str, (u64, u64, u64), SemverParseError<&str>> {
521    context(
522        "version core",
523        map(
524            tuple((number, tag("."), number, tag("."), number)),
525            |(major, _, minor, _, patch)| (major, minor, patch),
526        ),
527    )(input)
528}
529
530// I believe build, pre_release, and identifier are not 100% spec compliant.
531fn build(input: &str) -> IResult<&str, Vec<Identifier>, SemverParseError<&str>> {
532    context(
533        "build version",
534        preceded(tag("+"), separated_list1(tag("."), identifier)),
535    )(input)
536}
537
538fn pre_release(input: &str) -> IResult<&str, Vec<Identifier>, SemverParseError<&str>> {
539    context(
540        "pre_release version",
541        preceded(opt(tag("-")), separated_list1(tag("."), identifier)),
542    )(input)
543}
544
545fn identifier(input: &str) -> IResult<&str, Identifier, SemverParseError<&str>> {
546    context(
547        "identifier",
548        map(
549            take_while1(|x: char| is_alphanumeric(x as u8) || x == '-'),
550            |s: &str| {
551                str::parse::<u64>(s)
552                    .map(Identifier::Numeric)
553                    .unwrap_or_else(|_err| Identifier::AlphaNumeric(s.to_string()))
554            },
555        ),
556    )(input)
557}
558
559pub(crate) fn number(input: &str) -> IResult<&str, u64, SemverParseError<&str>> {
560    context(
561        "number component",
562        map_res(recognize(digit1), |raw| {
563            let value = str::parse(raw).map_err(|e| SemverParseError {
564                input,
565                context: None,
566                kind: Some(SemverErrorKind::ParseIntError(e)),
567            })?;
568
569            if value > MAX_SAFE_INTEGER {
570                return Err(SemverParseError {
571                    input,
572                    context: None,
573                    kind: Some(SemverErrorKind::MaxIntError(value)),
574                });
575            }
576
577            Ok(value)
578        }),
579    )(input)
580}
581
582#[cfg(test)]
583mod tests {
584    use super::Identifier::*;
585    use super::*;
586
587    use pretty_assertions::assert_eq;
588    use serde_derive::{Deserialize, Serialize};
589
590    #[test]
591    fn trivial_version_number() {
592        let v = Version::parse("1.2.34").unwrap();
593
594        assert_eq!(
595            v,
596            Version {
597                major: 1,
598                minor: 2,
599                patch: 34,
600                build: Vec::new(),
601                pre_release: Vec::new(),
602            }
603        );
604    }
605
606    #[test]
607    fn version_with_build() {
608        let v = Version::parse("1.2.34+123.456").unwrap();
609
610        assert_eq!(
611            v,
612            Version {
613                major: 1,
614                minor: 2,
615                patch: 34,
616                build: vec![Numeric(123), Numeric(456)],
617                pre_release: Vec::new(),
618            }
619        );
620    }
621
622    #[test]
623    fn version_with_pre_release() {
624        let v = Version::parse("1.2.34-abc.123").unwrap();
625
626        assert_eq!(
627            v,
628            Version {
629                major: 1,
630                minor: 2,
631                patch: 34,
632                pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
633                build: Vec::new(),
634            }
635        );
636    }
637
638    #[test]
639    fn version_with_pre_release_and_build() {
640        let v = Version::parse("1.2.34-abc.123+1").unwrap();
641
642        assert_eq!(
643            v,
644            Version {
645                major: 1,
646                minor: 2,
647                patch: 34,
648                pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
649                build: vec![Numeric(1),]
650            }
651        );
652    }
653
654    #[test]
655    fn pre_release_that_could_look_numeric_at_first() {
656        let v = Version::parse("1.0.0-rc.2-migration").unwrap();
657
658        assert_eq!(
659            v,
660            Version {
661                major: 1,
662                minor: 0,
663                patch: 0,
664                pre_release: vec![
665                    Identifier::AlphaNumeric("rc".into()),
666                    Identifier::AlphaNumeric("2-migration".into())
667                ],
668                build: vec![],
669            }
670        );
671    }
672
673    #[test]
674    fn comparison_with_different_major_version() {
675        let lesser_version = Version {
676            major: 1,
677            minor: 2,
678            patch: 34,
679            pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
680            build: vec![],
681        };
682        let greater_version = Version {
683            major: 2,
684            minor: 2,
685            patch: 34,
686            pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
687            build: vec![],
688        };
689        assert_eq!(lesser_version.cmp(&greater_version), Ordering::Less);
690        assert_eq!(greater_version.cmp(&lesser_version), Ordering::Greater);
691    }
692    #[test]
693    fn comparison_with_different_minor_version() {
694        let lesser_version = Version {
695            major: 1,
696            minor: 2,
697            patch: 34,
698            pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
699            build: vec![],
700        };
701        let greater_version = Version {
702            major: 1,
703            minor: 3,
704            patch: 34,
705            pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
706            build: vec![],
707        };
708        assert_eq!(lesser_version.cmp(&greater_version), Ordering::Less);
709        assert_eq!(greater_version.cmp(&lesser_version), Ordering::Greater);
710    }
711
712    #[test]
713    fn comparison_with_different_patch_version() {
714        let lesser_version = Version {
715            major: 1,
716            minor: 2,
717            patch: 34,
718            pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
719            build: vec![],
720        };
721        let greater_version = Version {
722            major: 1,
723            minor: 2,
724            patch: 56,
725            pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
726            build: vec![],
727        };
728        assert_eq!(lesser_version.cmp(&greater_version), Ordering::Less);
729        assert_eq!(greater_version.cmp(&lesser_version), Ordering::Greater);
730    }
731
732    #[test]
733    //confirms the comparison matches the pre-release comparison example in the SemVer spec.
734    //ie checks that 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.
735    //for simplicity just checks them in order. Assumes that the transitive property holds. So if a < b & b < c then a < c.
736    fn comparison_with_different_pre_release_version() {
737        let v1_alpha = Version {
738            major: 1,
739            minor: 0,
740            patch: 0,
741            pre_release: vec![AlphaNumeric("alpha".into())],
742            build: vec![],
743        };
744        let v1_alpha1 = Version {
745            major: 1,
746            minor: 0,
747            patch: 0,
748            pre_release: vec![AlphaNumeric("alpha".into()), Numeric(1)],
749            build: vec![],
750        };
751        assert_eq!(v1_alpha.cmp(&v1_alpha1), Ordering::Less);
752        let v1_alpha_beta = Version {
753            major: 1,
754            minor: 0,
755            patch: 0,
756            pre_release: vec![AlphaNumeric("alpha".into()), AlphaNumeric("beta".into())],
757            build: vec![],
758        };
759        assert_eq!(v1_alpha1.cmp(&v1_alpha_beta), Ordering::Less);
760        let v1_beta = Version {
761            major: 1,
762            minor: 0,
763            patch: 0,
764            pre_release: vec![AlphaNumeric("beta".into())],
765            build: vec![],
766        };
767        assert_eq!(v1_alpha_beta.cmp(&v1_beta), Ordering::Less);
768        let v1_beta2 = Version {
769            major: 1,
770            minor: 0,
771            patch: 0,
772            pre_release: vec![AlphaNumeric("beta".into()), Numeric(2)],
773            build: vec![],
774        };
775        assert_eq!(v1_beta.cmp(&v1_beta2), Ordering::Less);
776        let v1_beta11 = Version {
777            major: 1,
778            minor: 0,
779            patch: 0,
780            pre_release: vec![AlphaNumeric("beta".into()), Numeric(11)],
781            build: vec![],
782        };
783        assert_eq!(v1_beta2.cmp(&v1_beta11), Ordering::Less);
784        let v1_rc1 = Version {
785            major: 1,
786            minor: 0,
787            patch: 0,
788            pre_release: vec![AlphaNumeric("rc".into()), Numeric(1)],
789            build: vec![],
790        };
791        assert_eq!(v1_beta11.cmp(&v1_rc1), Ordering::Less);
792        let v1 = Version {
793            major: 1,
794            minor: 0,
795            patch: 0,
796            pre_release: vec![],
797            build: vec![],
798        };
799        assert_eq!(v1_rc1.cmp(&v1), Ordering::Less);
800    }
801
802    #[test]
803    fn individual_version_component_has_an_upper_bound() {
804        let out_of_range = MAX_SAFE_INTEGER + 1;
805        let v = Version::parse(format!("1.2.{}", out_of_range));
806        assert_eq!(v.err().expect("Parse should have failed.").to_string(), "Integer component of semver string is larger than JavaScript's Number.MAX_SAFE_INTEGER: 900719925474100");
807    }
808
809    #[test]
810    fn version_string_limited_to_256_characters() {
811        let prebuild = (0..257).map(|_| "X").collect::<Vec<_>>().join("");
812        let version_string = format!("1.1.1-{}", prebuild);
813        let v = Version::parse(version_string.clone());
814
815        assert_eq!(
816            v.err().expect("Parse should have failed").to_string(),
817            "Semver string can't be longer than 256 characters."
818        );
819
820        let ok_version = version_string[0..255].to_string();
821        let v = Version::parse(ok_version);
822        assert!(v.is_ok());
823    }
824
825    #[test]
826    fn version_prefixed_with_v() {
827        // TODO: This is part of strict parsing for node-semver!
828        let v = Version::parse("v1.2.3").unwrap();
829        assert_eq!(
830            v,
831            Version {
832                major: 1,
833                minor: 2,
834                patch: 3,
835                pre_release: vec![],
836                build: vec![],
837            }
838        );
839    }
840
841    #[test]
842    fn version_prefixed_with_v_space() {
843        // TODO: Loose parsing supports this, so
844        let v = Version::parse("v 1.2.3").unwrap();
845        assert_eq!(
846            v,
847            Version {
848                major: 1,
849                minor: 2,
850                patch: 3,
851                pre_release: vec![],
852                build: vec![],
853            }
854        );
855    }
856
857    #[derive(Serialize, Deserialize, Eq, PartialEq)]
858    struct Versioned {
859        version: Version,
860    }
861
862    #[test]
863    fn read_version_from_string() {
864        let v: Versioned = serde_json::from_str(r#"{"version":"1.2.34-abc.213+2"}"#).unwrap();
865
866        assert_eq!(
867            v.version,
868            Version {
869                major: 1,
870                minor: 2,
871                patch: 34,
872                pre_release: vec![
873                    Identifier::AlphaNumeric("abc".into()),
874                    Identifier::Numeric(213)
875                ],
876                build: vec![Identifier::Numeric(2)],
877            }
878        );
879    }
880
881    #[test]
882    fn serialize_a_version_to_string() {
883        let output = serde_json::to_string(&Versioned {
884            version: Version {
885                major: 1,
886                minor: 2,
887                patch: 34,
888                pre_release: vec![
889                    Identifier::AlphaNumeric("abc".into()),
890                    Identifier::Numeric(213),
891                ],
892                build: vec![Identifier::Numeric(2)],
893            },
894        })
895        .unwrap();
896        let expected: String = r#"{"version":"1.2.34-abc.213+2"}"#.into();
897
898        assert_eq!(output, expected);
899    }
900}