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()),
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")]
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 #[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#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
241pub enum Identifier {
242 Numeric(u64),
244 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#[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 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 pub fn satisfies(&self, range: &Range) -> bool {
283 range.satisfies(self)
284 }
285
286 pub fn is_prerelease(&self) -> bool {
288 !self.pre_release.is_empty()
289 }
290
291 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 order_result => return order_result,
451 }
452
453 match self.minor.cmp(&other.minor) {
454 Ordering::Equal => {}
455 order_result => return order_result,
457 }
458
459 match self.patch.cmp(&other.patch) {
460 Ordering::Equal => {}
461 order_result => return order_result,
463 }
464
465 match (self.pre_release.len(), other.pre_release.len()) {
466 (0, 0) => Ordering::Equal,
468 (0, _) => Ordering::Greater,
470 (_, 0) => Ordering::Less,
472 (_, _) => 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
495fn 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
531fn 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
542fn 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 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 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 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}