node_semver/
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()),
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    This error happens only when all of the range is invalid.
185    */
186    #[error("No valid ranges could be parsed")]
187    #[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."))]
188    NoValidRanges,
189
190    /**
191    This error is mostly nondescript. Feel free to file an issue if you run
192    into it.
193    */
194    #[error("An unspecified error occurred.")]
195    #[diagnostic(code(node_semver::other), url(docsrs))]
196    Other,
197}
198
199#[derive(Debug)]
200struct SemverParseError<I> {
201    pub(crate) input: I,
202    pub(crate) context: Option<&'static str>,
203    pub(crate) kind: Option<SemverErrorKind>,
204}
205
206impl<I> ParseError<I> for SemverParseError<I> {
207    fn from_error_kind(input: I, _kind: nom::error::ErrorKind) -> Self {
208        Self {
209            input,
210            context: None,
211            kind: None,
212        }
213    }
214
215    fn append(_input: I, _kind: nom::error::ErrorKind, other: Self) -> Self {
216        other
217    }
218}
219
220impl<I> ContextError<I> for SemverParseError<I> {
221    fn add_context(_input: I, ctx: &'static str, mut other: Self) -> Self {
222        other.context = Some(ctx);
223        other
224    }
225}
226
227impl<'a> FromExternalError<&'a str, SemverParseError<&'a str>> for SemverParseError<&'a str> {
228    fn from_external_error(
229        _input: &'a str,
230        _kind: ErrorKind,
231        e: SemverParseError<&'a str>,
232    ) -> Self {
233        e
234    }
235}
236
237/**
238An Identifier type for build and prerelease metadata.
239*/
240#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
241pub enum Identifier {
242    /// An identifier that's solely numbers.
243    Numeric(u64),
244    /// An identifier with letters and numbers.
245    AlphaNumeric(String),
246}
247
248impl fmt::Display for Identifier {
249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250        match self {
251            Identifier::Numeric(n) => write!(f, "{}", n),
252            Identifier::AlphaNumeric(s) => write!(f, "{}", s),
253        }
254    }
255}
256
257/**
258A semantic version, conformant to the [semver spec](https://semver.org/spec/v2.0.0.html).
259*/
260#[derive(Clone, Debug)]
261pub struct Version {
262    pub major: u64,
263    pub minor: u64,
264    pub patch: u64,
265    pub build: Vec<Identifier>,
266    pub pre_release: Vec<Identifier>,
267}
268
269impl Version {
270    /// Create a [Version] with a major, minor, and patch version.
271    pub const fn new(major: u64, minor: u64, patch: u64) -> Self {
272        Self {
273            major,
274            minor,
275            patch,
276            build: Vec::new(),
277            pre_release: Vec::new(),
278        }
279    }
280
281    /// True if this [Version] satisfies the given [Range].
282    pub fn satisfies(&self, range: &Range) -> bool {
283        range.satisfies(self)
284    }
285
286    /// True is this [Version] has a prerelease component.
287    pub fn is_prerelease(&self) -> bool {
288        !self.pre_release.is_empty()
289    }
290
291    /// Parse a semver string into a [Version].
292    pub fn parse<S: AsRef<str>>(input: S) -> Result<Version, SemverError> {
293        let input = input.as_ref();
294
295        if input.len() > MAX_LENGTH {
296            return Err(SemverError {
297                input: input.into(),
298                span: (input.len() - 1, 0).into(),
299                kind: SemverErrorKind::MaxLengthError,
300            });
301        }
302
303        match all_consuming(version)(input) {
304            Ok((_, arg)) => Ok(arg),
305            Err(err) => Err(match err {
306                Err::Error(e) | Err::Failure(e) => SemverError {
307                    input: input.into(),
308                    span: (e.input.as_ptr() as usize - input.as_ptr() as usize, 0).into(),
309                    kind: if let Some(kind) = e.kind {
310                        kind
311                    } else if let Some(ctx) = e.context {
312                        SemverErrorKind::Context(ctx)
313                    } else {
314                        SemverErrorKind::Other
315                    },
316                },
317                Err::Incomplete(_) => SemverError {
318                    input: input.into(),
319                    span: (input.len() - 1, 0).into(),
320                    kind: SemverErrorKind::IncompleteInput,
321                },
322            }),
323        }
324    }
325}
326
327impl PartialEq for Version {
328    fn eq(&self, other: &Self) -> bool {
329        self.major == other.major
330            && self.minor == other.minor
331            && self.patch == other.patch
332            && self.pre_release == other.pre_release
333    }
334}
335
336impl Eq for Version {}
337
338impl std::hash::Hash for Version {
339    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
340        self.major.hash(state);
341        self.minor.hash(state);
342        self.patch.hash(state);
343        self.pre_release.hash(state);
344    }
345}
346
347impl Serialize for Version {
348    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
349    where
350        S: Serializer,
351    {
352        serializer.collect_str(self)
353    }
354}
355
356impl<'de> Deserialize<'de> for Version {
357    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
358    where
359        D: Deserializer<'de>,
360    {
361        struct IntegrityVisitor;
362
363        impl Visitor<'_> for IntegrityVisitor {
364            type Value = Version;
365
366            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
367                formatter.write_str("a version string")
368            }
369
370            fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
371            where
372                E: de::Error,
373            {
374                Version::parse(v).map_err(de::Error::custom)
375            }
376        }
377
378        deserializer.deserialize_str(IntegrityVisitor)
379    }
380}
381
382impl fmt::Display for Version {
383    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
384        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?;
385
386        for (i, ident) in self.pre_release.iter().enumerate() {
387            if i == 0 {
388                write!(f, "-")?;
389            } else {
390                write!(f, ".")?;
391            }
392            write!(f, "{}", ident)?;
393        }
394
395        for (i, ident) in self.build.iter().enumerate() {
396            if i == 0 {
397                write!(f, "+")?;
398            } else {
399                write!(f, ".")?;
400            }
401            write!(f, "{}", ident)?;
402        }
403
404        Ok(())
405    }
406}
407
408impl std::convert::From<(u64, u64, u64)> for Version {
409    fn from((major, minor, patch): (u64, u64, u64)) -> Self {
410        Version {
411            major,
412            minor,
413            patch,
414            build: Vec::new(),
415            pre_release: Vec::new(),
416        }
417    }
418}
419
420impl std::str::FromStr for Version {
421    type Err = SemverError;
422    fn from_str(s: &str) -> Result<Self, Self::Err> {
423        Version::parse(s)
424    }
425}
426
427impl std::convert::From<(u64, u64, u64, u64)> for Version {
428    fn from((major, minor, patch, pre_release): (u64, u64, u64, u64)) -> Self {
429        Version {
430            major,
431            minor,
432            patch,
433            build: Vec::new(),
434            pre_release: vec![Identifier::Numeric(pre_release)],
435        }
436    }
437}
438
439impl cmp::PartialOrd for Version {
440    fn partial_cmp(&self, other: &Version) -> Option<Ordering> {
441        Some(self.cmp(other))
442    }
443}
444
445impl cmp::Ord for Version {
446    fn cmp(&self, other: &Version) -> cmp::Ordering {
447        match self.major.cmp(&other.major) {
448            Ordering::Equal => {}
449            //if difference in major version, just return result
450            order_result => return order_result,
451        }
452
453        match self.minor.cmp(&other.minor) {
454            Ordering::Equal => {}
455            //if difference in minor version, just return result
456            order_result => return order_result,
457        }
458
459        match self.patch.cmp(&other.patch) {
460            Ordering::Equal => {}
461            //if difference in patch version, just return result
462            order_result => return order_result,
463        }
464
465        match (self.pre_release.len(), other.pre_release.len()) {
466            //if no pre_release string, they're equal
467            (0, 0) => Ordering::Equal,
468            //if other has a pre-release string, but this doesn't, this one is greater
469            (0, _) => Ordering::Greater,
470            //if this one has a pre-release string, but other doesn't this one is less than
471            (_, 0) => Ordering::Less,
472            // if both have pre_release strings, compare the strings and return the result
473            (_, _) => self.pre_release.cmp(&other.pre_release),
474        }
475    }
476}
477
478enum Extras {
479    Build(Vec<Identifier>),
480    Release(Vec<Identifier>),
481    ReleaseAndBuild((Vec<Identifier>, Vec<Identifier>)),
482}
483
484impl Extras {
485    fn values(self) -> (Vec<Identifier>, Vec<Identifier>) {
486        use Extras::*;
487        match self {
488            Release(ident) => (ident, Vec::new()),
489            Build(ident) => (Vec::new(), ident),
490            ReleaseAndBuild(ident) => ident,
491        }
492    }
493}
494
495/// <valid semver> ::= <version core>
496///                 | <version core> "-" <pre-release>
497///                 | <version core> "+" <build>
498///                 | <version core> "-" <pre-release> "+" <build>
499fn version(input: &str) -> IResult<&str, Version, SemverParseError<&str>> {
500    context(
501        "version",
502        map(
503            tuple((opt(alt((tag("v"), tag("V")))), space0, version_core, extras)),
504            |(_, _, (major, minor, patch), (pre_release, build))| Version {
505                major,
506                minor,
507                patch,
508                pre_release,
509                build,
510            },
511        ),
512    )(input)
513}
514
515fn extras(
516    input: &str,
517) -> IResult<&str, (Vec<Identifier>, Vec<Identifier>), SemverParseError<&str>> {
518    map(
519        opt(alt((
520            map(tuple((pre_release, build)), Extras::ReleaseAndBuild),
521            map(pre_release, Extras::Release),
522            map(build, Extras::Build),
523        ))),
524        |extras| match extras {
525            Some(extras) => extras.values(),
526            _ => Default::default(),
527        },
528    )(input)
529}
530
531/// <version core> ::= <major> "." <minor> "." <patch>
532fn version_core(input: &str) -> IResult<&str, (u64, u64, u64), SemverParseError<&str>> {
533    context(
534        "version core",
535        map(
536            tuple((number, tag("."), number, tag("."), number)),
537            |(major, _, minor, _, patch)| (major, minor, patch),
538        ),
539    )(input)
540}
541
542// I believe build, pre_release, and identifier are not 100% spec compliant.
543fn build(input: &str) -> IResult<&str, Vec<Identifier>, SemverParseError<&str>> {
544    context(
545        "build version",
546        preceded(tag("+"), separated_list1(tag("."), identifier)),
547    )(input)
548}
549
550fn pre_release(input: &str) -> IResult<&str, Vec<Identifier>, SemverParseError<&str>> {
551    context(
552        "pre_release version",
553        preceded(opt(tag("-")), separated_list1(tag("."), identifier)),
554    )(input)
555}
556
557fn identifier(input: &str) -> IResult<&str, Identifier, SemverParseError<&str>> {
558    context(
559        "identifier",
560        map(
561            take_while1(|x: char| is_alphanumeric(x as u8) || x == '-'),
562            |s: &str| {
563                str::parse::<u64>(s)
564                    .map(Identifier::Numeric)
565                    .unwrap_or_else(|_err| Identifier::AlphaNumeric(s.to_string()))
566            },
567        ),
568    )(input)
569}
570
571pub(crate) fn number(input: &str) -> IResult<&str, u64, SemverParseError<&str>> {
572    context(
573        "number component",
574        map_res(recognize(digit1), |raw| {
575            let value = str::parse(raw).map_err(|e| SemverParseError {
576                input,
577                context: None,
578                kind: Some(SemverErrorKind::ParseIntError(e)),
579            })?;
580
581            if value > MAX_SAFE_INTEGER {
582                return Err(SemverParseError {
583                    input,
584                    context: None,
585                    kind: Some(SemverErrorKind::MaxIntError(value)),
586                });
587            }
588
589            Ok(value)
590        }),
591    )(input)
592}
593
594#[cfg(test)]
595mod tests {
596    use super::Identifier::*;
597    use super::*;
598
599    use pretty_assertions::assert_eq;
600    use serde_derive::{Deserialize, Serialize};
601
602    #[test]
603    fn trivial_version_number() {
604        let v = Version::parse("1.2.34").unwrap();
605
606        assert_eq!(
607            v,
608            Version {
609                major: 1,
610                minor: 2,
611                patch: 34,
612                build: Vec::new(),
613                pre_release: Vec::new(),
614            }
615        );
616    }
617
618    #[test]
619    fn version_with_build() {
620        let v = Version::parse("1.2.34+123.456").unwrap();
621
622        assert_eq!(
623            v,
624            Version {
625                major: 1,
626                minor: 2,
627                patch: 34,
628                build: vec![Numeric(123), Numeric(456)],
629                pre_release: Vec::new(),
630            }
631        );
632    }
633
634    #[test]
635    fn version_with_pre_release() {
636        let v = Version::parse("1.2.34-abc.123").unwrap();
637
638        assert_eq!(
639            v,
640            Version {
641                major: 1,
642                minor: 2,
643                patch: 34,
644                pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
645                build: Vec::new(),
646            }
647        );
648    }
649
650    #[test]
651    fn version_with_pre_release_and_build() {
652        let v = Version::parse("1.2.34-abc.123+1").unwrap();
653
654        assert_eq!(
655            v,
656            Version {
657                major: 1,
658                minor: 2,
659                patch: 34,
660                pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
661                build: vec![Numeric(1),]
662            }
663        );
664    }
665
666    #[test]
667    fn pre_release_that_could_look_numeric_at_first() {
668        let v = Version::parse("1.0.0-rc.2-migration").unwrap();
669
670        assert_eq!(
671            v,
672            Version {
673                major: 1,
674                minor: 0,
675                patch: 0,
676                pre_release: vec![
677                    Identifier::AlphaNumeric("rc".into()),
678                    Identifier::AlphaNumeric("2-migration".into())
679                ],
680                build: vec![],
681            }
682        );
683    }
684
685    #[test]
686    fn comparison_with_different_major_version() {
687        let lesser_version = Version {
688            major: 1,
689            minor: 2,
690            patch: 34,
691            pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
692            build: vec![],
693        };
694        let greater_version = Version {
695            major: 2,
696            minor: 2,
697            patch: 34,
698            pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
699            build: vec![],
700        };
701        assert_eq!(lesser_version.cmp(&greater_version), Ordering::Less);
702        assert_eq!(greater_version.cmp(&lesser_version), Ordering::Greater);
703    }
704    #[test]
705    fn comparison_with_different_minor_version() {
706        let lesser_version = Version {
707            major: 1,
708            minor: 2,
709            patch: 34,
710            pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
711            build: vec![],
712        };
713        let greater_version = Version {
714            major: 1,
715            minor: 3,
716            patch: 34,
717            pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
718            build: vec![],
719        };
720        assert_eq!(lesser_version.cmp(&greater_version), Ordering::Less);
721        assert_eq!(greater_version.cmp(&lesser_version), Ordering::Greater);
722    }
723
724    #[test]
725    fn comparison_with_different_patch_version() {
726        let lesser_version = Version {
727            major: 1,
728            minor: 2,
729            patch: 34,
730            pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
731            build: vec![],
732        };
733        let greater_version = Version {
734            major: 1,
735            minor: 2,
736            patch: 56,
737            pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
738            build: vec![],
739        };
740        assert_eq!(lesser_version.cmp(&greater_version), Ordering::Less);
741        assert_eq!(greater_version.cmp(&lesser_version), Ordering::Greater);
742    }
743
744    #[test]
745    //confirms the comparison matches the pre-release comparison example in the SemVer spec.
746    //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.
747    //for simplicity just checks them in order. Assumes that the transitive property holds. So if a < b & b < c then a < c.
748    fn comparison_with_different_pre_release_version() {
749        let v1_alpha = Version {
750            major: 1,
751            minor: 0,
752            patch: 0,
753            pre_release: vec![AlphaNumeric("alpha".into())],
754            build: vec![],
755        };
756        let v1_alpha1 = Version {
757            major: 1,
758            minor: 0,
759            patch: 0,
760            pre_release: vec![AlphaNumeric("alpha".into()), Numeric(1)],
761            build: vec![],
762        };
763        assert_eq!(v1_alpha.cmp(&v1_alpha1), Ordering::Less);
764        let v1_alpha_beta = Version {
765            major: 1,
766            minor: 0,
767            patch: 0,
768            pre_release: vec![AlphaNumeric("alpha".into()), AlphaNumeric("beta".into())],
769            build: vec![],
770        };
771        assert_eq!(v1_alpha1.cmp(&v1_alpha_beta), Ordering::Less);
772        let v1_beta = Version {
773            major: 1,
774            minor: 0,
775            patch: 0,
776            pre_release: vec![AlphaNumeric("beta".into())],
777            build: vec![],
778        };
779        assert_eq!(v1_alpha_beta.cmp(&v1_beta), Ordering::Less);
780        let v1_beta2 = Version {
781            major: 1,
782            minor: 0,
783            patch: 0,
784            pre_release: vec![AlphaNumeric("beta".into()), Numeric(2)],
785            build: vec![],
786        };
787        assert_eq!(v1_beta.cmp(&v1_beta2), Ordering::Less);
788        let v1_beta11 = Version {
789            major: 1,
790            minor: 0,
791            patch: 0,
792            pre_release: vec![AlphaNumeric("beta".into()), Numeric(11)],
793            build: vec![],
794        };
795        assert_eq!(v1_beta2.cmp(&v1_beta11), Ordering::Less);
796        let v1_rc1 = Version {
797            major: 1,
798            minor: 0,
799            patch: 0,
800            pre_release: vec![AlphaNumeric("rc".into()), Numeric(1)],
801            build: vec![],
802        };
803        assert_eq!(v1_beta11.cmp(&v1_rc1), Ordering::Less);
804        let v1 = Version {
805            major: 1,
806            minor: 0,
807            patch: 0,
808            pre_release: vec![],
809            build: vec![],
810        };
811        assert_eq!(v1_rc1.cmp(&v1), Ordering::Less);
812    }
813
814    #[test]
815    fn individual_version_component_has_an_upper_bound() {
816        let out_of_range = MAX_SAFE_INTEGER + 1;
817        let v = Version::parse(format!("1.2.{}", out_of_range));
818        assert_eq!(v.expect_err("Parse should have failed.").to_string(), "Integer component of semver string is larger than JavaScript's Number.MAX_SAFE_INTEGER: 900719925474100");
819    }
820
821    #[test]
822    fn version_string_limited_to_256_characters() {
823        let prebuild = (0..257).map(|_| "X").collect::<Vec<_>>().join("");
824        let version_string = format!("1.1.1-{}", prebuild);
825        let v = Version::parse(version_string.clone());
826
827        assert_eq!(
828            v.expect_err("Parse should have failed").to_string(),
829            "Semver string can't be longer than 256 characters."
830        );
831
832        let ok_version = version_string[0..255].to_string();
833        let v = Version::parse(ok_version);
834        assert!(v.is_ok());
835    }
836
837    #[test]
838    fn version_prefixed_with_v() {
839        // TODO: This is part of strict parsing for node-semver!
840        let v = Version::parse("v1.2.3").unwrap();
841        assert_eq!(
842            v,
843            Version {
844                major: 1,
845                minor: 2,
846                patch: 3,
847                pre_release: vec![],
848                build: vec![],
849            }
850        );
851    }
852
853    #[test]
854    fn version_prefixed_with_v_space() {
855        // TODO: Loose parsing supports this, so
856        let v = Version::parse("v 1.2.3").unwrap();
857        assert_eq!(
858            v,
859            Version {
860                major: 1,
861                minor: 2,
862                patch: 3,
863                pre_release: vec![],
864                build: vec![],
865            }
866        );
867    }
868
869    #[derive(Serialize, Deserialize, Eq, PartialEq)]
870    struct Versioned {
871        version: Version,
872    }
873
874    #[test]
875    fn read_version_from_string() {
876        let v: Versioned = serde_json::from_str(r#"{"version":"1.2.34-abc.213+2"}"#).unwrap();
877
878        assert_eq!(
879            v.version,
880            Version {
881                major: 1,
882                minor: 2,
883                patch: 34,
884                pre_release: vec![
885                    Identifier::AlphaNumeric("abc".into()),
886                    Identifier::Numeric(213)
887                ],
888                build: vec![Identifier::Numeric(2)],
889            }
890        );
891    }
892
893    #[test]
894    fn serialize_a_version_to_string() {
895        let output = serde_json::to_string(&Versioned {
896            version: Version {
897                major: 1,
898                minor: 2,
899                patch: 34,
900                pre_release: vec![
901                    Identifier::AlphaNumeric("abc".into()),
902                    Identifier::Numeric(213),
903                ],
904                build: vec![Identifier::Numeric(2)],
905            },
906        })
907        .unwrap();
908        let expected: String = r#"{"version":"1.2.34-abc.213+2"}"#.into();
909
910        assert_eq!(output, expected);
911    }
912}