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
28pub const MAX_SAFE_INTEGER: u64 = 900_719_925_474_099;
33
34pub const MAX_LENGTH: usize = 256;
36
37#[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 pub fn input(&self) -> &str {
82 &self.input
83 }
84
85 pub fn span(&self) -> &SourceSpan {
87 &self.span
88 }
89
90 pub fn offset(&self) -> usize {
92 self.span.offset()
93 }
94
95 pub fn kind(&self) -> &SemverErrorKind {
100 &self.kind
101 }
102
103 pub fn location(&self) -> (usize, usize) {
106 let prefix = &self.input.as_bytes()[..self.offset()];
108
109 let line_number = bytecount::count(prefix, b'\n');
111
112 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 let line = self.input[line_begin..]
123 .lines()
124 .next()
125 .unwrap_or(&self.input[line_begin..])
126 .trim_end();
127
128 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#[derive(Debug, Clone, Error, Eq, PartialEq, Diagnostic)]
139pub enum SemverErrorKind {
140 #[error("Semver string can't be longer than {} characters.", MAX_LENGTH)]
145 #[diagnostic(code(node_semver::too_long), url(docsrs))]
146 MaxLengthError,
147
148 #[error("Incomplete input to semver parser.")]
155 #[diagnostic(code(node_semver::incomplete_input), url(docsrs))]
156 IncompleteInput,
157
158 #[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 #[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 #[error("Failed to parse {0}.")]
180 #[diagnostic(code(node_semver::parse_component_error), url(docsrs))]
181 Context(&'static str),
182
183 #[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 #[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#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
240pub enum Identifier {
241 Numeric(u64),
243 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#[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 pub fn satisfies(&self, range: &Range) -> bool {
271 range.satisfies(self)
272 }
273
274 pub fn is_prerelease(&self) -> bool {
276 !self.pre_release.is_empty()
277 }
278
279 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 order_result => return order_result,
439 }
440
441 match self.minor.cmp(&other.minor) {
442 Ordering::Equal => {}
443 order_result => return order_result,
445 }
446
447 match self.patch.cmp(&other.patch) {
448 Ordering::Equal => {}
449 order_result => return order_result,
451 }
452
453 match (self.pre_release.len(), other.pre_release.len()) {
454 (0, 0) => Ordering::Equal,
456 (0, _) => Ordering::Greater,
458 (_, 0) => Ordering::Less,
460 (_, _) => 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
483fn 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
519fn 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
530fn 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 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 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 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}