1#![doc = include_str!("../README.md")]
2#![allow(clippy::needless_doctest_main)]
3
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize, de::Deserializer, ser::Serializer};
6
7use std::cmp::{self, Ordering};
8use std::fmt;
9use std::num::ParseIntError;
10
11use miette::{Diagnostic, SourceSpan};
12use thiserror::Error;
13
14use winnow::ascii::{digit1, space0};
15use winnow::combinator::{alt, opt, preceded, separated};
16use winnow::error::{AddContext, ErrMode, FromExternalError, ParserError};
17use winnow::stream::{AsChar, Stream};
18use winnow::token::{literal, take_while};
19use winnow::{ModalResult, Parser};
20
21pub use range::*;
22
23mod range;
24mod version_fast;
25
26pub const MAX_SAFE_INTEGER: u64 = 900_719_925_474_099;
31
32pub const MAX_LENGTH: usize = 256;
34
35#[derive(Debug, Clone, Error, Eq, PartialEq)]
42#[error("{kind}")]
43pub struct SemverError {
44 input: String,
45 span: SourceSpan,
46 kind: SemverErrorKind,
47}
48
49impl Diagnostic for SemverError {
50 fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
51 self.kind().code()
52 }
53
54 fn severity(&self) -> Option<miette::Severity> {
55 self.kind().severity()
56 }
57
58 fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
59 self.kind().help()
60 }
61
62 fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
63 self.kind().url()
64 }
65
66 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
67 Some(&self.input)
68 }
69
70 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
71 Some(Box::new(std::iter::once(
72 miette::LabeledSpan::new_with_span(Some("here".into()), *self.span()),
73 )))
74 }
75}
76
77impl SemverError {
78 pub fn input(&self) -> &str {
80 &self.input
81 }
82
83 pub fn span(&self) -> &SourceSpan {
85 &self.span
86 }
87
88 pub fn offset(&self) -> usize {
90 self.span.offset()
91 }
92
93 pub fn kind(&self) -> &SemverErrorKind {
98 &self.kind
99 }
100
101 pub fn location(&self) -> (usize, usize) {
104 let prefix = &self.input.as_bytes()[..self.offset()];
106
107 let line_number = bytecount::count(prefix, b'\n');
109
110 let line_begin = prefix
113 .iter()
114 .rev()
115 .position(|&b| b == b'\n')
116 .map(|pos| self.offset() - pos)
117 .unwrap_or(0);
118
119 let line = self.input[line_begin..]
121 .lines()
122 .next()
123 .unwrap_or(&self.input[line_begin..])
124 .trim_end();
125
126 let column_number = self.input[self.offset()..].as_ptr() as usize - line.as_ptr() as usize;
128
129 (line_number, column_number)
130 }
131}
132
133#[derive(Debug, Clone, Error, Eq, PartialEq, Diagnostic)]
137pub enum SemverErrorKind {
138 #[error("Semver string can't be longer than {} characters.", MAX_LENGTH)]
143 #[diagnostic(code(nodejs_semver::too_long), url(docsrs))]
144 MaxLengthError,
145
146 #[error("Incomplete input to semver parser.")]
153 #[diagnostic(code(nodejs_semver::incomplete_input), url(docsrs))]
154 IncompleteInput,
155
156 #[error("Failed to parse an integer component of a semver string: {0}")]
162 #[diagnostic(code(nodejs_semver::parse_int_error), url(docsrs))]
163 ParseIntError(ParseIntError),
164
165 #[error(
170 "Integer component of semver string is larger than JavaScript's Number.MAX_SAFE_INTEGER: {0}"
171 )]
172 #[diagnostic(code(nodejs_semver::integer_too_large), url(docsrs))]
173 MaxIntError(u64),
174
175 #[error("Failed to parse {0}.")]
180 #[diagnostic(code(nodejs_semver::parse_component_error), url(docsrs))]
181 Context(&'static str),
182
183 #[error("No valid ranges could be parsed")]
184 #[diagnostic(
185 code(nodejs_semver::no_valid_ranges),
186 url(docsrs),
187 help(
188 "nodejs-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."
189 )
190 )]
191 NoValidRanges,
192
193 #[error("An unspecified error occurred.")]
198 #[diagnostic(code(nodejs_semver::other), url(docsrs))]
199 Other,
200}
201
202#[derive(Debug)]
203struct SemverParseError<I> {
204 pub(crate) input: I,
205 pub(crate) context: Option<&'static str>,
206 pub(crate) kind: Option<SemverErrorKind>,
207}
208
209impl<I: Clone + Stream> ParserError<I> for SemverParseError<I> {
210 type Inner = Self;
211
212 fn from_input(input: &I) -> Self {
213 Self {
214 input: input.clone(),
215 context: None,
216 kind: None,
217 }
218 }
219
220 fn append(self, input: &I, _token_start: &<I as Stream>::Checkpoint) -> Self {
221 Self {
222 input: input.clone(),
223 context: self.context,
224 kind: self.kind,
225 }
226 }
227
228 fn into_inner(self) -> Result<Self::Inner, Self> {
229 Ok(self)
230 }
231}
232
233impl<I: Stream> AddContext<I> for SemverParseError<I> {
234 fn add_context(
235 self,
236 _input: &I,
237 _token_start: &<I as Stream>::Checkpoint,
238 ctx: &'static str,
239 ) -> Self {
240 Self {
241 input: self.input,
242 context: Some(ctx),
243 kind: self.kind,
244 }
245 }
246}
247
248impl<'a> FromExternalError<&'a str, SemverParseError<&'a str>> for SemverParseError<&'a str> {
249 fn from_external_error(_input: &&'a str, e: SemverParseError<&'a str>) -> Self {
250 e
251 }
252}
253
254#[cold]
255#[inline(never)]
256fn max_length_error(input: &str) -> SemverError {
257 SemverError {
258 input: input.into(),
259 span: (input.len() - 1, 0).into(),
260 kind: SemverErrorKind::MaxLengthError,
261 }
262}
263
264#[cold]
265#[inline(never)]
266pub(crate) fn semver_error_from_parse(
267 input: &str,
268 err: ErrMode<SemverParseError<&str>>,
269) -> SemverError {
270 match err {
271 ErrMode::Backtrack(e) | ErrMode::Cut(e) => SemverError {
272 input: input.into(),
273 span: (e.input.as_ptr() as usize - input.as_ptr() as usize, 0).into(),
274 kind: if let Some(kind) = e.kind {
275 kind
276 } else if let Some(ctx) = e.context {
277 SemverErrorKind::Context(ctx)
278 } else {
279 SemverErrorKind::Other
280 },
281 },
282 ErrMode::Incomplete(_) => SemverError {
283 input: input.into(),
284 span: (input.len() - 1, 0).into(),
285 kind: SemverErrorKind::IncompleteInput,
286 },
287 }
288}
289
290#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
294pub enum Identifier {
295 Numeric(u64),
297 AlphaNumeric(String),
299}
300
301impl fmt::Display for Identifier {
302 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303 match self {
304 Identifier::Numeric(n) => write!(f, "{}", n),
305 Identifier::AlphaNumeric(s) => write!(f, "{}", s),
306 }
307 }
308}
309
310#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
312pub enum VersionDiff {
313 Major,
314 Minor,
315 Patch,
316 PreMajor,
317 PreMinor,
318 PrePatch,
319 PreRelease,
320}
321
322pub type ReleaseType = VersionDiff;
324
325#[derive(Debug, Clone, Copy, PartialEq, Eq)]
326pub enum IdentifierBase {
327 False,
329 Value(u64),
331}
332
333impl Default for IdentifierBase {
334 fn default() -> Self {
335 IdentifierBase::Value(0)
336 }
337}
338
339impl IdentifierBase {
340 fn base_value(self) -> u64 {
341 match self {
342 IdentifierBase::False => 0,
343 IdentifierBase::Value(v) => {
344 if v == 0 {
345 0
346 } else {
347 1
348 }
349 }
350 }
351 }
352}
353
354impl From<bool> for IdentifierBase {
355 fn from(value: bool) -> Self {
356 if value {
357 IdentifierBase::Value(1)
358 } else {
359 IdentifierBase::False
360 }
361 }
362}
363
364impl From<u64> for IdentifierBase {
365 fn from(value: u64) -> Self {
366 IdentifierBase::Value(value)
367 }
368}
369
370impl From<usize> for IdentifierBase {
371 fn from(value: usize) -> Self {
372 IdentifierBase::Value(value as u64)
373 }
374}
375
376#[derive(Debug, Clone, PartialEq, Eq, Error)]
377pub enum IncrementError {
378 #[error("invalid identifier: {0}")]
379 InvalidIdentifier(String),
380 #[error("invalid increment argument: {0}")]
381 InvalidIncrementArgument(String),
382 #[error("version {0} is not a prerelease")]
383 NotAPrerelease(String),
384 #[error("increment would overflow a version component")]
385 Overflow,
386}
387
388impl fmt::Display for VersionDiff {
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 match self {
391 VersionDiff::Major => write!(f, "major"),
392 VersionDiff::Minor => write!(f, "minor"),
393 VersionDiff::Patch => write!(f, "patch"),
394 VersionDiff::PreMajor => write!(f, "premajor"),
395 VersionDiff::PreMinor => write!(f, "preminor"),
396 VersionDiff::PrePatch => write!(f, "prepatch"),
397 VersionDiff::PreRelease => write!(f, "prerelease"),
398 }
399 }
400}
401
402#[derive(Clone, Debug)]
406pub struct Version {
407 major: u64,
408 minor: u64,
409 patch: u64,
410 meta: Option<Box<VersionMeta>>,
411}
412
413#[derive(Clone, Debug, Eq, PartialEq)]
415pub struct VersionParts {
416 pub major: u64,
417 pub minor: u64,
418 pub patch: u64,
419 pub pre_release: Vec<Identifier>,
420 pub build: Vec<Identifier>,
421}
422
423#[derive(Clone, Debug)]
424struct VersionMeta {
425 build: Identifiers,
426 pre_release: Identifiers,
427}
428
429#[derive(Clone, Debug)]
430pub(crate) enum Identifiers {
431 Empty,
432 One(Identifier),
433 Two([Identifier; 2]),
434 Many(Vec<Identifier>),
435}
436
437impl Identifiers {
438 pub(crate) fn from_vec(mut identifiers: Vec<Identifier>) -> Self {
439 match identifiers.len() {
440 0 => Self::Empty,
441 1 => Self::One(identifiers.pop().unwrap()),
442 2 => {
443 let second = identifiers.pop().unwrap();
444 let first = identifiers.pop().unwrap();
445 Self::Two([first, second])
446 }
447 _ => Self::Many(identifiers),
448 }
449 }
450
451 fn into_vec(self) -> Vec<Identifier> {
452 match self {
453 Self::Empty => Vec::new(),
454 Self::One(identifier) => vec![identifier],
455 Self::Two(identifiers) => Vec::from(identifiers),
456 Self::Many(identifiers) => identifiers,
457 }
458 }
459
460 fn as_slice(&self) -> &[Identifier] {
461 match self {
462 Self::Empty => &[],
463 Self::One(identifier) => std::slice::from_ref(identifier),
464 Self::Two(identifiers) => identifiers.as_slice(),
465 Self::Many(identifiers) => identifiers.as_slice(),
466 }
467 }
468
469 fn as_mut_slice(&mut self) -> &mut [Identifier] {
470 match self {
471 Self::Empty => &mut [],
472 Self::One(identifier) => std::slice::from_mut(identifier),
473 Self::Two(identifiers) => identifiers.as_mut_slice(),
474 Self::Many(identifiers) => identifiers.as_mut_slice(),
475 }
476 }
477
478 fn is_empty(&self) -> bool {
479 matches!(self, Self::Empty)
480 }
481
482 fn clear(&mut self) {
483 *self = Self::Empty;
484 }
485
486 pub(crate) fn push(&mut self, identifier: Identifier) {
487 match std::mem::replace(self, Self::Empty) {
488 Self::Empty => *self = Self::One(identifier),
489 Self::One(existing) => *self = Self::Two([existing, identifier]),
490 Self::Two([first, second]) => *self = Self::Many(vec![first, second, identifier]),
491 Self::Many(mut identifiers) => {
492 identifiers.push(identifier);
493 *self = Self::Many(identifiers);
494 }
495 }
496 }
497}
498
499#[cfg(feature = "serde")]
500impl Serialize for Version {
501 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
502 s.collect_str(self)
503 }
504}
505
506#[cfg(feature = "serde")]
507impl<'de> Deserialize<'de> for Version {
508 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
509 let s = String::deserialize(d)?;
510 s.parse().map_err(serde::de::Error::custom)
511 }
512}
513
514impl Version {
515 pub fn major(&self) -> u64 {
517 self.major
518 }
519
520 pub fn minor(&self) -> u64 {
522 self.minor
523 }
524
525 pub fn patch(&self) -> u64 {
527 self.patch
528 }
529
530 pub fn new(
531 major: u64,
532 minor: u64,
533 patch: u64,
534 pre_release: Vec<Identifier>,
535 build: Vec<Identifier>,
536 ) -> Self {
537 let meta = if pre_release.is_empty() && build.is_empty() {
538 return Self::new_empty(major, minor, patch);
539 } else {
540 Some(Box::new(VersionMeta {
541 build: Identifiers::from_vec(build),
542 pre_release: Identifiers::from_vec(pre_release),
543 }))
544 };
545
546 Self {
547 major,
548 minor,
549 patch,
550 meta,
551 }
552 }
553
554 pub(crate) fn new_empty(major: u64, minor: u64, patch: u64) -> Self {
555 Self {
556 major,
557 minor,
558 patch,
559 meta: None,
560 }
561 }
562
563 fn new_with_pre_release_identifier(
564 major: u64,
565 minor: u64,
566 patch: u64,
567 pre_release: Identifier,
568 ) -> Self {
569 Self {
570 major,
571 minor,
572 patch,
573 meta: Some(Box::new(VersionMeta {
574 build: Identifiers::Empty,
575 pre_release: Identifiers::One(pre_release),
576 })),
577 }
578 }
579
580 pub(crate) fn new_with_identifiers(
581 major: u64,
582 minor: u64,
583 patch: u64,
584 pre_release: Identifiers,
585 build: Identifiers,
586 ) -> Self {
587 if pre_release.is_empty() && build.is_empty() {
588 return Self::new_empty(major, minor, patch);
589 }
590
591 Self {
592 major,
593 minor,
594 patch,
595 meta: Some(Box::new(VersionMeta { build, pre_release })),
596 }
597 }
598
599 pub fn build(&self) -> &[Identifier] {
600 self.meta.as_ref().map_or(&[], |meta| meta.build.as_slice())
601 }
602
603 pub fn pre_release(&self) -> &[Identifier] {
604 self.meta
605 .as_ref()
606 .map_or(&[], |meta| meta.pre_release.as_slice())
607 }
608
609 pub fn into_parts(self) -> VersionParts {
639 let Version {
640 major,
641 minor,
642 patch,
643 meta,
644 } = self;
645
646 let (pre_release, build) = match meta {
647 Some(meta) => {
648 let VersionMeta { build, pre_release } = *meta;
649 (pre_release.into_vec(), build.into_vec())
650 }
651 None => (Vec::new(), Vec::new()),
652 };
653
654 VersionParts {
655 major,
656 minor,
657 patch,
658 pre_release,
659 build,
660 }
661 }
662
663 fn metadata_mut(&mut self) -> &mut VersionMeta {
664 self.meta.get_or_insert_with(|| {
665 Box::new(VersionMeta {
666 build: Identifiers::Empty,
667 pre_release: Identifiers::Empty,
668 })
669 })
670 }
671
672 fn clear_pre_release(&mut self) {
673 if let Some(meta) = &mut self.meta {
674 meta.pre_release.clear();
675 if meta.build.is_empty() {
676 self.meta = None;
677 }
678 }
679 }
680
681 pub(crate) fn push_pre_release(&mut self, identifier: Identifier) {
682 self.metadata_mut().pre_release.push(identifier);
683 }
684
685 fn set_pre_release(&mut self, pre_release: Vec<Identifier>) {
686 if pre_release.is_empty() {
687 self.clear_pre_release();
688 } else {
689 self.metadata_mut().pre_release = Identifiers::from_vec(pre_release);
690 }
691 }
692
693 fn pre_release_mut(&mut self) -> &mut Identifiers {
694 &mut self.metadata_mut().pre_release
695 }
696
697 pub fn satisfies(&self, range: &Range) -> bool {
699 range.satisfies(self)
700 }
701
702 pub fn satisfies_with_prerelease(&self, range: &Range, include_prerelease: bool) -> bool {
706 range.satisfies_with_prerelease(self, include_prerelease)
707 }
708
709 pub fn is_prerelease(&self) -> bool {
711 !self.pre_release().is_empty()
712 }
713
714 pub fn inc(
718 &self,
719 release: &str,
720 identifier: Option<&str>,
721 identifier_base: Option<IdentifierBase>,
722 ) -> Result<Version, IncrementError> {
723 let mut cloned = self.clone();
724 cloned.inc_mut(release, identifier, identifier_base)?;
725 Ok(cloned)
726 }
727
728 fn inc_mut(
729 &mut self,
730 release: &str,
731 identifier: Option<&str>,
732 identifier_base: Option<IdentifierBase>,
733 ) -> Result<&mut Self, IncrementError> {
734 let identifier_base = identifier_base.unwrap_or_default();
735 let identifier_base_is_false = identifier_base == IdentifierBase::False;
736 let identifier = identifier.and_then(|id| {
737 if id.is_empty() {
738 None
739 } else {
740 Some(id.to_string())
741 }
742 });
743
744 if release.starts_with("pre") {
745 if identifier.is_none() && identifier_base_is_false {
746 return Err(IncrementError::InvalidIncrementArgument(
747 "identifier is empty".into(),
748 ));
749 }
750 if let Some(id) = identifier.as_deref() {
751 if !is_valid_prerelease_identifier(id) {
752 return Err(IncrementError::InvalidIdentifier(id.to_string()));
753 }
754 }
755 }
756
757 match release {
758 "premajor" => {
759 self.clear_pre_release();
760 self.patch = 0;
761 self.minor = 0;
762 self.major = self.major.checked_add(1).ok_or(IncrementError::Overflow)?;
763 self.inc_mut("pre", identifier.as_deref(), Some(identifier_base))?;
764 }
765 "preminor" => {
766 self.clear_pre_release();
767 self.patch = 0;
768 self.minor = self.minor.checked_add(1).ok_or(IncrementError::Overflow)?;
769 self.inc_mut("pre", identifier.as_deref(), Some(identifier_base))?;
770 }
771 "prepatch" => {
772 self.clear_pre_release();
773 self.inc_mut("patch", identifier.as_deref(), Some(identifier_base))?;
774 self.inc_mut("pre", identifier.as_deref(), Some(identifier_base))?;
775 }
776 "prerelease" => {
777 if self.pre_release().is_empty() {
778 self.inc_mut("patch", identifier.as_deref(), Some(identifier_base))?;
779 }
780 self.inc_mut("pre", identifier.as_deref(), Some(identifier_base))?;
781 }
782 "release" => {
783 if self.pre_release().is_empty() {
784 return Err(IncrementError::NotAPrerelease(self.to_string()));
785 }
786 self.clear_pre_release();
787 }
788 "major" => {
789 if self.minor != 0 || self.patch != 0 || self.pre_release().is_empty() {
790 self.major = self.major.checked_add(1).ok_or(IncrementError::Overflow)?;
791 }
792 self.minor = 0;
793 self.patch = 0;
794 self.clear_pre_release();
795 }
796 "minor" => {
797 if self.patch != 0 || self.pre_release().is_empty() {
798 self.minor = self.minor.checked_add(1).ok_or(IncrementError::Overflow)?;
799 }
800 self.patch = 0;
801 self.clear_pre_release();
802 }
803 "patch" => {
804 if self.pre_release().is_empty() {
805 self.patch = self.patch.checked_add(1).ok_or(IncrementError::Overflow)?;
806 }
807 self.clear_pre_release();
808 }
809 "pre" => {
810 self.apply_pre_increment(identifier.as_deref(), identifier_base)?;
811 }
812 _ => {
813 return Err(IncrementError::InvalidIncrementArgument(
814 release.to_string(),
815 ));
816 }
817 }
818
819 Ok(self)
820 }
821
822 fn apply_pre_increment(
823 &mut self,
824 identifier: Option<&str>,
825 identifier_base: IdentifierBase,
826 ) -> Result<(), IncrementError> {
827 let base = identifier_base.base_value();
828 let identifier_base_is_false = identifier_base == IdentifierBase::False;
829 let identifier = identifier.map(|id| id.to_string());
830
831 if self.pre_release().is_empty() {
832 self.push_pre_release(Identifier::Numeric(base));
833 } else {
834 let mut incremented = false;
835 for ident in self.pre_release_mut().as_mut_slice().iter_mut().rev() {
836 if let Identifier::Numeric(num) = ident {
837 *num = num.checked_add(1).ok_or(IncrementError::Overflow)?;
838 incremented = true;
839 break;
840 }
841 }
842
843 if !incremented {
844 if identifier_base_is_false {
845 if let Some(id) = identifier.as_deref() {
846 if id == join_prerelease_components(self.pre_release()) {
847 return Err(IncrementError::InvalidIncrementArgument(
848 "identifier already exists".into(),
849 ));
850 }
851 }
852 }
853 self.push_pre_release(Identifier::Numeric(base));
854 }
855 }
856
857 if let Some(id) = identifier {
858 let prerelease = if identifier_base_is_false {
859 vec![Identifier::AlphaNumeric(id.clone())]
860 } else {
861 vec![
862 Identifier::AlphaNumeric(id.clone()),
863 Identifier::Numeric(base),
864 ]
865 };
866
867 if let Some(first) = self.pre_release().first() {
868 if compare_identifier_and_str(first, &id) == Ordering::Equal {
869 if !matches!(self.pre_release().get(1), Some(Identifier::Numeric(_))) {
870 self.set_pre_release(prerelease);
871 }
872 } else {
873 self.set_pre_release(prerelease);
874 }
875 } else {
876 self.set_pre_release(prerelease);
877 }
878 }
879
880 Ok(())
881 }
882
883 #[doc = include_str!("../examples/parse.rs")]
887 pub fn parse<S: AsRef<str>>(input: S) -> Result<Version, SemverError> {
889 let mut input = input.as_ref();
890
891 if input.len() > MAX_LENGTH {
892 return Err(max_length_error(input));
893 }
894
895 if let Some(version) = version_fast::parse(input) {
896 return Ok(version);
897 }
898
899 match version.parse_next(&mut input) {
900 Ok(version) => Ok(version),
901 Err(err) => Err(semver_error_from_parse(input, err)),
902 }
903 }
904
905 #[doc = include_str!("../examples/diff.rs")]
910 pub fn diff(&self, other: &Self) -> Option<VersionDiff> {
912 let cmp_result = self.cmp(other);
913
914 if cmp_result == Ordering::Equal {
915 return None;
916 }
917
918 let self_higher = cmp_result == Ordering::Greater;
919 let high_version = if self_higher { self } else { other };
920 let low_version = if self_higher { other } else { self };
921 let high_has_pre = high_version.is_prerelease();
922 let low_has_pre = low_version.is_prerelease();
923
924 if low_has_pre && !high_has_pre {
925 if low_version.patch == 0 && low_version.minor == 0 {
933 return Some(VersionDiff::Major);
934 }
935
936 if high_version.patch != 0 {
938 return Some(VersionDiff::Patch);
940 }
941
942 if high_version.minor != 0 {
943 return Some(VersionDiff::Minor);
945 }
946
947 return Some(VersionDiff::Major);
949 }
950
951 if self.major != other.major {
952 if high_has_pre {
953 return Some(VersionDiff::PreMajor);
954 }
955
956 return Some(VersionDiff::Major);
957 }
958
959 if self.minor != other.minor {
960 if high_has_pre {
961 return Some(VersionDiff::PreMinor);
962 }
963
964 return Some(VersionDiff::Minor);
965 }
966
967 if self.patch != other.patch {
968 if high_has_pre {
969 return Some(VersionDiff::PrePatch);
970 }
971
972 return Some(VersionDiff::Patch);
973 }
974
975 Some(VersionDiff::PreRelease)
977 }
978}
979
980impl PartialEq for Version {
981 fn eq(&self, other: &Self) -> bool {
982 self.major == other.major
983 && self.minor == other.minor
984 && self.patch == other.patch
985 && self.pre_release() == other.pre_release()
986 }
987}
988
989impl Eq for Version {}
990
991impl std::hash::Hash for Version {
992 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
993 self.major.hash(state);
994 self.minor.hash(state);
995 self.patch.hash(state);
996 self.pre_release().hash(state);
997 }
998}
999
1000impl fmt::Display for Version {
1001 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1002 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?;
1003
1004 for (i, ident) in self.pre_release().iter().enumerate() {
1005 if i == 0 {
1006 write!(f, "-")?;
1007 } else {
1008 write!(f, ".")?;
1009 }
1010 write!(f, "{}", ident)?;
1011 }
1012
1013 for (i, ident) in self.build().iter().enumerate() {
1014 if i == 0 {
1015 write!(f, "+")?;
1016 } else {
1017 write!(f, ".")?;
1018 }
1019 write!(f, "{}", ident)?;
1020 }
1021
1022 Ok(())
1023 }
1024}
1025
1026macro_rules! impl_from_unsigned_for_version {
1027 ($($t:ident),+) => {
1028 $(
1029 impl ::std::convert::From<($t, $t, $t)> for Version {
1030 fn from((major, minor, patch): ($t, $t, $t)) -> Self {
1031 Version::new_empty(major as u64, minor as u64, patch as u64)
1032 }
1033 }
1034
1035 impl ::std::convert::From<($t, $t, $t, $t)> for Version {
1036 fn from((major, minor, patch, pre_release): ($t, $t, $t, $t)) -> Self {
1037 Version::new_with_pre_release_identifier(
1038 major as u64,
1039 minor as u64,
1040 patch as u64,
1041 Identifier::Numeric(pre_release as u64),
1042 )
1043 }
1044 }
1045 )+
1046 }
1047}
1048
1049macro_rules! impl_from_signed_for_version {
1050 ($($t:ident),+) => {
1051 $(
1052 impl ::std::convert::From<($t, $t, $t)> for Version {
1053 fn from((major, minor, patch): ($t, $t, $t)) -> Self {
1054 debug_assert!(major >= 0, "Version major must be non-negative, got {}", major);
1055 debug_assert!(minor >= 0, "Version minor must be non-negative, got {}", minor);
1056 debug_assert!(patch >= 0, "Version patch must be non-negative, got {}", patch);
1057
1058 Version::new_empty(major as u64, minor as u64, patch as u64)
1059 }
1060 }
1061
1062 impl ::std::convert::From<($t, $t, $t, $t)> for Version {
1063 fn from((major, minor, patch, pre_release): ($t, $t, $t, $t)) -> Self {
1064 debug_assert!(major >= 0, "Version major must be non-negative, got {}", major);
1065 debug_assert!(minor >= 0, "Version minor must be non-negative, got {}", minor);
1066 debug_assert!(patch >= 0, "Version patch must be non-negative, got {}", patch);
1067 debug_assert!(pre_release >= 0, "Version pre-release must be non-negative, got {}", pre_release);
1068
1069 Version::new_with_pre_release_identifier(
1070 major as u64,
1071 minor as u64,
1072 patch as u64,
1073 Identifier::Numeric(pre_release as u64),
1074 )
1075 }
1076 }
1077 )+
1078 }
1079}
1080
1081impl_from_unsigned_for_version!(u8, u16, u32, u64, usize);
1082impl_from_signed_for_version!(i8, i16, i32, i64, isize);
1083
1084impl std::str::FromStr for Version {
1085 type Err = SemverError;
1086 fn from_str(s: &str) -> Result<Self, Self::Err> {
1087 Version::parse(s)
1088 }
1089}
1090
1091impl cmp::PartialOrd for Version {
1092 fn partial_cmp(&self, other: &Version) -> Option<Ordering> {
1093 Some(self.cmp(other))
1094 }
1095}
1096
1097impl cmp::Ord for Version {
1098 fn cmp(&self, other: &Version) -> cmp::Ordering {
1099 match self.major.cmp(&other.major) {
1100 Ordering::Equal => {}
1101 order_result => return order_result,
1103 }
1104
1105 match self.minor.cmp(&other.minor) {
1106 Ordering::Equal => {}
1107 order_result => return order_result,
1109 }
1110
1111 match self.patch.cmp(&other.patch) {
1112 Ordering::Equal => {}
1113 order_result => return order_result,
1115 }
1116
1117 match (self.pre_release().len(), other.pre_release().len()) {
1118 (0, 0) => Ordering::Equal,
1120 (0, _) => Ordering::Greater,
1122 (_, 0) => Ordering::Less,
1124 (_, _) => self.pre_release().cmp(other.pre_release()),
1126 }
1127 }
1128}
1129
1130enum Extras {
1131 Build(Vec<Identifier>),
1132 Release(Vec<Identifier>),
1133 ReleaseAndBuild((Vec<Identifier>, Vec<Identifier>)),
1134}
1135
1136impl Extras {
1137 fn values(self) -> (Vec<Identifier>, Vec<Identifier>) {
1138 use Extras::*;
1139 match self {
1140 Release(ident) => (ident, Vec::new()),
1141 Build(ident) => (Vec::new(), ident),
1142 ReleaseAndBuild(ident) => ident,
1143 }
1144 }
1145}
1146
1147fn version<'s>(input: &mut &'s str) -> ModalResult<Version, SemverParseError<&'s str>> {
1152 (
1153 opt(alt((literal("v"), literal("V")))),
1154 space0,
1155 version_core,
1156 extras,
1157 )
1158 .map(|(_, _, (major, minor, patch), (pre_release, build))| {
1159 Version::new(major, minor, patch, pre_release, build)
1160 })
1161 .context("version")
1162 .parse_next(input)
1163}
1164
1165fn extras<'s>(
1166 input: &mut &'s str,
1167) -> ModalResult<(Vec<Identifier>, Vec<Identifier>), SemverParseError<&'s str>> {
1168 Parser::map(
1169 opt(alt((
1170 Parser::map((pre_release, build), Extras::ReleaseAndBuild),
1171 Parser::map(pre_release, Extras::Release),
1172 Parser::map(build, Extras::Build),
1173 ))),
1174 |extras| match extras {
1175 Some(extras) => extras.values(),
1176 _ => Default::default(),
1177 },
1178 )
1179 .parse_next(input)
1180}
1181
1182fn version_core<'s>(
1184 input: &mut &'s str,
1185) -> ModalResult<(u64, u64, u64), SemverParseError<&'s str>> {
1186 (number, literal("."), number, literal("."), number)
1187 .map(|(major, _, minor, _, patch)| (major, minor, patch))
1188 .context("version core")
1189 .parse_next(input)
1190}
1191
1192fn build<'s>(input: &mut &'s str) -> ModalResult<Vec<Identifier>, SemverParseError<&'s str>> {
1194 preceded(literal("+"), separated(1.., identifier, literal(".")))
1195 .context("build version")
1196 .parse_next(input)
1197}
1198
1199fn pre_release<'s>(input: &mut &'s str) -> ModalResult<Vec<Identifier>, SemverParseError<&'s str>> {
1200 preceded(opt(literal("-")), separated(1.., identifier, literal(".")))
1201 .context("pre_release version")
1202 .parse_next(input)
1203}
1204
1205fn identifier<'s>(input: &mut &'s str) -> ModalResult<Identifier, SemverParseError<&'s str>> {
1206 Parser::map(
1207 take_while(1.., |x: char| AsChar::is_alphanum(x as u8) || x == '-'),
1208 |s: &str| {
1209 str::parse::<u64>(s)
1210 .map(Identifier::Numeric)
1211 .unwrap_or_else(|_err| Identifier::AlphaNumeric(s.to_string()))
1212 },
1213 )
1214 .context("identifier")
1215 .parse_next(input)
1216}
1217
1218fn is_valid_prerelease_identifier(identifier: &str) -> bool {
1219 !identifier.is_empty()
1220 && identifier.split('.').all(|segment| {
1221 !segment.is_empty()
1222 && segment
1223 .chars()
1224 .all(|ch| ch.is_ascii_alphanumeric() || ch == '-')
1225 })
1226}
1227
1228fn join_prerelease_components(pre_release: &[Identifier]) -> String {
1229 pre_release
1230 .iter()
1231 .map(|ident| ident.to_string())
1232 .collect::<Vec<_>>()
1233 .join(".")
1234}
1235
1236fn compare_identifier_and_str(existing: &Identifier, other: &str) -> Ordering {
1237 match existing {
1238 Identifier::Numeric(value) => {
1239 if other.chars().all(|c| c.is_ascii_digit()) {
1240 match other.parse::<u128>() {
1241 Ok(other_num) => (*value as u128).cmp(&other_num),
1242 Err(_) => Ordering::Less,
1243 }
1244 } else {
1245 Ordering::Less
1246 }
1247 }
1248 Identifier::AlphaNumeric(value) => {
1249 if other.chars().all(|c| c.is_ascii_digit()) {
1250 Ordering::Greater
1251 } else {
1252 value.as_str().cmp(other)
1253 }
1254 }
1255 }
1256}
1257
1258pub(crate) fn number<'s>(input: &mut &'s str) -> ModalResult<u64, SemverParseError<&'s str>> {
1259 #[allow(suspicious_double_ref_op)]
1260 let copied = input.clone();
1261
1262 Parser::try_map(Parser::take(digit1), |raw| {
1263 let value = parse_u64_digits(raw).map_err(|e| SemverParseError {
1264 input: copied,
1265 context: None,
1266 kind: Some(SemverErrorKind::ParseIntError(e)),
1267 })?;
1268
1269 if value > MAX_SAFE_INTEGER {
1270 return Err(SemverParseError {
1271 input: copied,
1272 context: None,
1273 kind: Some(SemverErrorKind::MaxIntError(value)),
1274 });
1275 }
1276
1277 Ok(value)
1278 })
1279 .context("number component")
1280 .parse_next(input)
1281}
1282
1283fn parse_u64_digits(raw: &str) -> Result<u64, ParseIntError> {
1284 let mut value = 0u64;
1285
1286 for ch in raw.bytes() {
1287 let digit = u64::from(ch - b'0');
1288 let Some(next) = value
1289 .checked_mul(10)
1290 .and_then(|value| value.checked_add(digit))
1291 else {
1292 return raw.parse::<u64>();
1293 };
1294 value = next;
1295 }
1296
1297 Ok(value)
1298}
1299
1300#[cfg(test)]
1301mod tests {
1302 use super::Identifier::*;
1303 use super::*;
1304
1305 use pretty_assertions::assert_eq;
1306
1307 #[derive(Debug)]
1308 struct IncrementCase {
1309 version: String,
1310 release: String,
1311 expected: Option<String>,
1312 identifier: Option<String>,
1313 identifier_base: Option<IdentifierBase>,
1314 }
1315
1316 #[derive(Debug)]
1317 struct IncrementCaseRaw {
1318 version: &'static str,
1319 release: &'static str,
1320 expected: Option<&'static str>,
1321 identifier: Option<&'static str>,
1322 identifier_base: Option<IdentifierBase>,
1323 }
1324
1325 macro_rules! ic {
1326 ($v:literal, $r:literal, $e:literal) => {
1327 IncrementCaseRaw {
1328 version: $v,
1329 release: $r,
1330 expected: Some($e),
1331 identifier: None,
1332 identifier_base: None,
1333 }
1334 };
1335 ($v:literal, $r:literal, None) => {
1336 IncrementCaseRaw {
1337 version: $v,
1338 release: $r,
1339 expected: None,
1340 identifier: None,
1341 identifier_base: None,
1342 }
1343 };
1344 ($v:literal, $r:literal, $e:literal, $id:literal) => {
1345 IncrementCaseRaw {
1346 version: $v,
1347 release: $r,
1348 expected: Some($e),
1349 identifier: Some($id),
1350 identifier_base: None,
1351 }
1352 };
1353 ($v:literal, $r:literal, None, $id:literal) => {
1354 IncrementCaseRaw {
1355 version: $v,
1356 release: $r,
1357 expected: None,
1358 identifier: Some($id),
1359 identifier_base: None,
1360 }
1361 };
1362 ($v:literal, $r:literal, $e:literal, $id:literal, $base:expr) => {
1363 IncrementCaseRaw {
1364 version: $v,
1365 release: $r,
1366 expected: Some($e),
1367 identifier: Some($id),
1368 identifier_base: Some($base),
1369 }
1370 };
1371 ($v:literal, $r:literal, None, $id:literal, $base:expr) => {
1372 IncrementCaseRaw {
1373 version: $v,
1374 release: $r,
1375 expected: None,
1376 identifier: Some($id),
1377 identifier_base: Some($base),
1378 }
1379 };
1380 }
1381
1382 static INCREMENT_CASES: &[IncrementCaseRaw] = &[
1384 ic!("1.2.3", "major", "2.0.0"),
1385 ic!("1.2.3", "minor", "1.3.0"),
1386 ic!("1.2.3", "patch", "1.2.4"),
1387 ic!("1.2.3tag", "major", "2.0.0"),
1388 ic!("1.2.3-tag", "major", "2.0.0"),
1389 ic!("1.2.3", "fake", None),
1390 ic!("1.2.0-0", "patch", "1.2.0"),
1391 ic!("fake", "major", None),
1392 ic!("1.2.3-4", "major", "2.0.0"),
1393 ic!("1.2.3-4", "minor", "1.3.0"),
1394 ic!("1.2.3-4", "patch", "1.2.3"),
1395 ic!("1.2.3-alpha.0.beta", "major", "2.0.0"),
1396 ic!("1.2.3-alpha.0.beta", "minor", "1.3.0"),
1397 ic!("1.2.3-alpha.0.beta", "patch", "1.2.3"),
1398 ic!("1.2.4", "prerelease", "1.2.5-0"),
1399 ic!("1.2.3-0", "prerelease", "1.2.3-1"),
1400 ic!("1.2.3-alpha.0", "prerelease", "1.2.3-alpha.1"),
1401 ic!("1.2.3-alpha.1", "prerelease", "1.2.3-alpha.2"),
1402 ic!("1.2.3-alpha.2", "prerelease", "1.2.3-alpha.3"),
1403 ic!("1.2.3-alpha.0.beta", "prerelease", "1.2.3-alpha.1.beta"),
1404 ic!("1.2.3-alpha.1.beta", "prerelease", "1.2.3-alpha.2.beta"),
1405 ic!("1.2.3-alpha.2.beta", "prerelease", "1.2.3-alpha.3.beta"),
1406 ic!(
1407 "1.2.3-alpha.10.0.beta",
1408 "prerelease",
1409 "1.2.3-alpha.10.1.beta"
1410 ),
1411 ic!(
1412 "1.2.3-alpha.10.1.beta",
1413 "prerelease",
1414 "1.2.3-alpha.10.2.beta"
1415 ),
1416 ic!(
1417 "1.2.3-alpha.10.2.beta",
1418 "prerelease",
1419 "1.2.3-alpha.10.3.beta"
1420 ),
1421 ic!(
1422 "1.2.3-alpha.10.beta.0",
1423 "prerelease",
1424 "1.2.3-alpha.10.beta.1"
1425 ),
1426 ic!(
1427 "1.2.3-alpha.10.beta.1",
1428 "prerelease",
1429 "1.2.3-alpha.10.beta.2"
1430 ),
1431 ic!(
1432 "1.2.3-alpha.10.beta.2",
1433 "prerelease",
1434 "1.2.3-alpha.10.beta.3"
1435 ),
1436 ic!("1.2.3-alpha.9.beta", "prerelease", "1.2.3-alpha.10.beta"),
1437 ic!("1.2.3-alpha.10.beta", "prerelease", "1.2.3-alpha.11.beta"),
1438 ic!("1.2.3-alpha.11.beta", "prerelease", "1.2.3-alpha.12.beta"),
1439 ic!("1.0.0", "prepatch", "1.0.1-alpha.1.1a.0", "alpha.1.1a"),
1440 ic!("1.2.0", "prepatch", "1.2.1-0"),
1441 ic!("1.2.0-1", "prepatch", "1.2.1-0"),
1442 ic!("1.2.0", "preminor", "1.3.0-0"),
1443 ic!("1.2.3-1", "preminor", "1.3.0-0"),
1444 ic!("1.2.0", "premajor", "2.0.0-0"),
1445 ic!("1.2.3-1", "premajor", "2.0.0-0"),
1446 ic!("1.2.0-1", "minor", "1.2.0"),
1447 ic!("1.0.0-1", "major", "1.0.0"),
1448 ic!("1.0.0-1", "release", "1.0.0"),
1449 ic!("1.2.0-1", "release", "1.2.0"),
1450 ic!("1.2.3-1", "release", "1.2.3"),
1451 ic!("1.2.3", "release", None),
1452 ic!("1.2.3", "major", "2.0.0", "dev"),
1453 ic!("1.2.3", "minor", "1.3.0", "dev"),
1454 ic!("1.2.3", "patch", "1.2.4", "dev"),
1455 ic!("1.2.3tag", "major", "2.0.0", "dev"),
1456 ic!("1.2.3-tag", "major", "2.0.0", "dev"),
1457 ic!("1.2.3", "fake", None, "dev"),
1458 ic!("1.2.0-0", "patch", "1.2.0", "dev"),
1459 ic!("fake", "major", None, "dev"),
1460 ic!("1.2.3-4", "major", "2.0.0", "dev"),
1461 ic!("1.2.3-4", "minor", "1.3.0", "dev"),
1462 ic!("1.2.3-4", "patch", "1.2.3", "dev"),
1463 ic!("1.2.3-alpha.0.beta", "major", "2.0.0", "dev"),
1464 ic!("1.2.3-alpha.0.beta", "minor", "1.3.0", "dev"),
1465 ic!("1.2.3-alpha.0.beta", "patch", "1.2.3", "dev"),
1466 ic!("1.2.4", "prerelease", "1.2.5-dev.0", "dev"),
1467 ic!("1.2.3-0", "prerelease", "1.2.3-dev.0", "dev"),
1468 ic!("1.2.3-alpha.0", "prerelease", "1.2.3-dev.0", "dev"),
1469 ic!("1.2.3-alpha.0", "prerelease", "1.2.3-alpha.1", "alpha"),
1470 ic!("1.2.3-alpha.0.beta", "prerelease", "1.2.3-dev.0", "dev"),
1471 ic!(
1472 "1.2.3-alpha.0.beta",
1473 "prerelease",
1474 "1.2.3-alpha.1.beta",
1475 "alpha"
1476 ),
1477 ic!("1.2.3-alpha.10.0.beta", "prerelease", "1.2.3-dev.0", "dev"),
1478 ic!(
1479 "1.2.3-alpha.10.0.beta",
1480 "prerelease",
1481 "1.2.3-alpha.10.1.beta",
1482 "alpha"
1483 ),
1484 ic!(
1485 "1.2.3-alpha.10.1.beta",
1486 "prerelease",
1487 "1.2.3-alpha.10.2.beta",
1488 "alpha"
1489 ),
1490 ic!(
1491 "1.2.3-alpha.10.2.beta",
1492 "prerelease",
1493 "1.2.3-alpha.10.3.beta",
1494 "alpha"
1495 ),
1496 ic!("1.2.3-alpha.10.beta.0", "prerelease", "1.2.3-dev.0", "dev"),
1497 ic!(
1498 "1.2.3-alpha.10.beta.0",
1499 "prerelease",
1500 "1.2.3-alpha.10.beta.1",
1501 "alpha"
1502 ),
1503 ic!(
1504 "1.2.3-alpha.10.beta.1",
1505 "prerelease",
1506 "1.2.3-alpha.10.beta.2",
1507 "alpha"
1508 ),
1509 ic!(
1510 "1.2.3-alpha.10.beta.2",
1511 "prerelease",
1512 "1.2.3-alpha.10.beta.3",
1513 "alpha"
1514 ),
1515 ic!("1.2.3-alpha.9.beta", "prerelease", "1.2.3-dev.0", "dev"),
1516 ic!(
1517 "1.2.3-alpha.9.beta",
1518 "prerelease",
1519 "1.2.3-alpha.10.beta",
1520 "alpha"
1521 ),
1522 ic!(
1523 "1.2.3-alpha.10.beta",
1524 "prerelease",
1525 "1.2.3-alpha.11.beta",
1526 "alpha"
1527 ),
1528 ic!(
1529 "1.2.3-alpha.11.beta",
1530 "prerelease",
1531 "1.2.3-alpha.12.beta",
1532 "alpha"
1533 ),
1534 ic!("1.2.0", "prepatch", "1.2.1-dev.0", "dev"),
1535 ic!("1.2.0-1", "prepatch", "1.2.1-dev.0", "dev"),
1536 ic!("1.2.0", "preminor", "1.3.0-dev.0", "dev"),
1537 ic!("1.2.3-1", "preminor", "1.3.0-dev.0", "dev"),
1538 ic!("1.2.0", "premajor", "2.0.0-dev.0", "dev"),
1539 ic!("1.2.3-1", "premajor", "2.0.0-dev.0", "dev"),
1540 ic!(
1541 "1.2.3-1",
1542 "premajor",
1543 "2.0.0-dev.1",
1544 "dev",
1545 IdentifierBase::Value(1)
1546 ),
1547 ic!("1.2.0-1", "minor", "1.2.0", "dev"),
1548 ic!("1.0.0-1", "major", "1.0.0", "dev"),
1549 ic!("1.2.3-dev.bar", "prerelease", "1.2.3-dev.0", "dev"),
1550 ic!("1.2.3-0", "prerelease", "1.2.3-1.0", "1"),
1551 ic!("1.2.3-1.0", "prerelease", "1.2.3-1.1", "1"),
1552 ic!("1.2.3-1.1", "prerelease", "1.2.3-1.2", "1"),
1553 ic!("1.2.3-1.1", "prerelease", "1.2.3-2.0", "2"),
1554 ic!(
1555 "1.2.0-1",
1556 "prerelease",
1557 "1.2.0-alpha.0",
1558 "alpha",
1559 IdentifierBase::Value(0)
1560 ),
1561 ic!(
1562 "1.2.1",
1563 "prerelease",
1564 "1.2.2-alpha.0",
1565 "alpha",
1566 IdentifierBase::Value(0)
1567 ),
1568 ic!(
1569 "0.2.0",
1570 "prerelease",
1571 "0.2.1-alpha.0",
1572 "alpha",
1573 IdentifierBase::Value(0)
1574 ),
1575 ic!(
1576 "1.2.2",
1577 "prerelease",
1578 "1.2.3-alpha.1",
1579 "alpha",
1580 IdentifierBase::Value(1)
1581 ),
1582 ic!(
1583 "1.2.3",
1584 "prerelease",
1585 "1.2.4-alpha.1",
1586 "alpha",
1587 IdentifierBase::Value(1)
1588 ),
1589 ic!(
1590 "1.2.4",
1591 "prerelease",
1592 "1.2.5-alpha.1",
1593 "alpha",
1594 IdentifierBase::Value(1)
1595 ),
1596 ic!(
1597 "1.2.0",
1598 "prepatch",
1599 "1.2.1-dev.1",
1600 "dev",
1601 IdentifierBase::Value(1)
1602 ),
1603 ic!(
1604 "1.2.0-1",
1605 "prepatch",
1606 "1.2.1-dev.1",
1607 "dev",
1608 IdentifierBase::Value(1)
1609 ),
1610 ic!(
1611 "1.2.0",
1612 "premajor",
1613 "2.0.0-dev.0",
1614 "dev",
1615 IdentifierBase::Value(0)
1616 ),
1617 ic!(
1618 "1.2.3-1",
1619 "premajor",
1620 "2.0.0-dev.0",
1621 "dev",
1622 IdentifierBase::Value(0)
1623 ),
1624 ic!(
1625 "1.2.3-dev.bar",
1626 "prerelease",
1627 "1.2.3-dev.0",
1628 "dev",
1629 IdentifierBase::Value(0)
1630 ),
1631 ic!(
1632 "1.2.3-dev.bar",
1633 "prerelease",
1634 "1.2.3-dev.1",
1635 "dev",
1636 IdentifierBase::Value(1)
1637 ),
1638 ic!(
1639 "1.2.3-dev.bar",
1640 "prerelease",
1641 "1.2.3-dev.bar.0",
1642 "",
1643 IdentifierBase::Value(0)
1644 ),
1645 ic!(
1646 "1.2.3-dev.bar",
1647 "prerelease",
1648 "1.2.3-dev.bar.1",
1649 "",
1650 IdentifierBase::Value(1)
1651 ),
1652 ic!(
1653 "1.2.0",
1654 "preminor",
1655 "1.3.0-dev.1",
1656 "dev",
1657 IdentifierBase::Value(1)
1658 ),
1659 ic!("1.2.3-1", "preminor", "1.3.0-dev.0", "dev"),
1660 ic!(
1661 "1.2.0",
1662 "prerelease",
1663 "1.2.1-1",
1664 "",
1665 IdentifierBase::Value(1)
1666 ),
1667 ic!(
1668 "1.2.0-1",
1669 "prerelease",
1670 "1.2.0-alpha",
1671 "alpha",
1672 IdentifierBase::False
1673 ),
1674 ic!(
1675 "1.2.1",
1676 "prerelease",
1677 "1.2.2-alpha",
1678 "alpha",
1679 IdentifierBase::False
1680 ),
1681 ic!(
1682 "1.2.2",
1683 "prerelease",
1684 "1.2.3-alpha",
1685 "alpha",
1686 IdentifierBase::False
1687 ),
1688 ic!(
1689 "1.2.0",
1690 "prepatch",
1691 "1.2.1-dev",
1692 "dev",
1693 IdentifierBase::False
1694 ),
1695 ic!(
1696 "1.2.0-1",
1697 "prepatch",
1698 "1.2.1-dev",
1699 "dev",
1700 IdentifierBase::False
1701 ),
1702 ic!(
1703 "1.2.0",
1704 "premajor",
1705 "2.0.0-dev",
1706 "dev",
1707 IdentifierBase::False
1708 ),
1709 ic!(
1710 "1.2.3-1",
1711 "premajor",
1712 "2.0.0-dev",
1713 "dev",
1714 IdentifierBase::False
1715 ),
1716 ic!(
1717 "1.2.3-dev.bar",
1718 "prerelease",
1719 "1.2.3-dev",
1720 "dev",
1721 IdentifierBase::False
1722 ),
1723 ic!(
1724 "1.2.3-dev.bar",
1725 "prerelease",
1726 "1.2.3-dev.baz",
1727 "dev.baz",
1728 IdentifierBase::False
1729 ),
1730 ic!(
1731 "1.2.0",
1732 "preminor",
1733 "1.3.0-dev",
1734 "dev",
1735 IdentifierBase::False
1736 ),
1737 ic!(
1738 "1.2.3-1",
1739 "preminor",
1740 "1.3.0-dev",
1741 "dev",
1742 IdentifierBase::False
1743 ),
1744 ic!(
1745 "1.2.3-dev",
1746 "prerelease",
1747 None,
1748 "dev",
1749 IdentifierBase::False
1750 ),
1751 ic!(
1752 "1.2.0-dev",
1753 "premajor",
1754 "2.0.0-dev",
1755 "dev",
1756 IdentifierBase::False
1757 ),
1758 ic!(
1759 "1.2.0-dev",
1760 "preminor",
1761 "1.3.0-beta",
1762 "beta",
1763 IdentifierBase::False
1764 ),
1765 ic!(
1766 "1.2.0-dev",
1767 "prepatch",
1768 "1.2.1-dev",
1769 "dev",
1770 IdentifierBase::False
1771 ),
1772 ic!("1.2.0", "prerelease", None, "", IdentifierBase::False),
1773 ic!(
1774 "1.0.0-rc.1+build.4",
1775 "prerelease",
1776 "1.0.0-rc.2",
1777 "rc",
1778 IdentifierBase::False
1779 ),
1780 ic!("1.2.0", "prerelease", None, "invalid/preid"),
1781 ic!("1.2.0", "prerelease", None, "invalid+build"),
1782 ic!("1.2.0beta", "prerelease", None, "invalid/preid"),
1783 ];
1784
1785 fn version_without_build(version: &Version) -> String {
1786 let mut output = format!("{}.{}.{}", version.major, version.minor, version.patch);
1787 if !version.pre_release().is_empty() {
1788 output.push('-');
1789 output.push_str(&join_prerelease_components(version.pre_release()));
1790 }
1791 output
1792 }
1793
1794 fn load_increment_cases() -> Vec<IncrementCase> {
1795 INCREMENT_CASES
1796 .iter()
1797 .map(|raw| IncrementCase {
1798 version: raw.version.to_string(),
1799 release: raw.release.to_string(),
1800 expected: raw.expected.map(str::to_string),
1801 identifier: raw.identifier.map(str::to_string),
1802 identifier_base: raw.identifier_base,
1803 })
1804 .collect()
1805 }
1806
1807 #[test]
1808 fn trivial_version_number() {
1809 let v = Version::parse("1.2.34").unwrap();
1810
1811 assert_eq!((v.major(), v.minor(), v.patch()), (1, 2, 34));
1812 assert_eq!(
1813 v,
1814 Version::new(1, 2, 34, Vec::with_capacity(2), Vec::with_capacity(2))
1815 );
1816 }
1817
1818 #[test]
1819 fn version_with_build() {
1820 let v = Version::parse("1.2.34+123.456").unwrap();
1821
1822 assert_eq!(
1823 v,
1824 Version::new(
1825 1,
1826 2,
1827 34,
1828 Vec::with_capacity(2),
1829 vec![Numeric(123), Numeric(456)]
1830 )
1831 );
1832 }
1833
1834 #[test]
1835 fn version_with_pre_release() {
1836 let v = Version::parse("1.2.34-abc.123").unwrap();
1837
1838 assert_eq!(
1839 v,
1840 Version::new(
1841 1,
1842 2,
1843 34,
1844 vec![AlphaNumeric("abc".into()), Numeric(123)],
1845 Vec::with_capacity(2),
1846 )
1847 );
1848 }
1849
1850 #[test]
1851 fn version_with_pre_release_and_build() {
1852 let v = Version::parse("1.2.34-abc.123+1").unwrap();
1853
1854 assert_eq!(
1855 v,
1856 Version::new(
1857 1,
1858 2,
1859 34,
1860 vec![AlphaNumeric("abc".into()), Numeric(123)],
1861 vec![Numeric(1)],
1862 )
1863 );
1864 }
1865
1866 #[test]
1867 fn into_parts_with_metadata() {
1868 let parts = Version::parse("1.2.34-abc.123+1").unwrap().into_parts();
1869
1870 assert_eq!(
1871 parts,
1872 VersionParts {
1873 major: 1,
1874 minor: 2,
1875 patch: 34,
1876 pre_release: vec![AlphaNumeric("abc".into()), Numeric(123)],
1877 build: vec![Numeric(1)],
1878 }
1879 );
1880 }
1881
1882 #[test]
1883 fn into_parts_without_metadata() {
1884 let parts = Version::parse("1.2.34").unwrap().into_parts();
1885
1886 assert_eq!(
1887 parts,
1888 VersionParts {
1889 major: 1,
1890 minor: 2,
1891 patch: 34,
1892 pre_release: Vec::new(),
1893 build: Vec::new(),
1894 }
1895 );
1896 }
1897
1898 #[test]
1899 fn pre_release_that_could_look_numeric_at_first() {
1900 let v = Version::parse("1.0.0-rc.2-migration").unwrap();
1901
1902 assert_eq!(
1903 v,
1904 Version::new(
1905 1,
1906 0,
1907 0,
1908 vec![
1909 Identifier::AlphaNumeric("rc".into()),
1910 Identifier::AlphaNumeric("2-migration".into())
1911 ],
1912 vec![],
1913 )
1914 );
1915 }
1916
1917 #[test]
1918 fn comparison_with_different_major_version() {
1919 let lesser_version = Version::new(
1920 1,
1921 2,
1922 34,
1923 vec![AlphaNumeric("abc".into()), Numeric(123)],
1924 vec![],
1925 );
1926 let greater_version = Version::new(
1927 2,
1928 2,
1929 34,
1930 vec![AlphaNumeric("abc".into()), Numeric(123)],
1931 vec![],
1932 );
1933 assert_eq!(lesser_version.cmp(&greater_version), Ordering::Less);
1934 assert_eq!(greater_version.cmp(&lesser_version), Ordering::Greater);
1935 }
1936 #[test]
1937 fn comparison_with_different_minor_version() {
1938 let lesser_version = Version::new(
1939 1,
1940 2,
1941 34,
1942 vec![AlphaNumeric("abc".into()), Numeric(123)],
1943 vec![],
1944 );
1945 let greater_version = Version::new(
1946 1,
1947 3,
1948 34,
1949 vec![AlphaNumeric("abc".into()), Numeric(123)],
1950 vec![],
1951 );
1952 assert_eq!(lesser_version.cmp(&greater_version), Ordering::Less);
1953 assert_eq!(greater_version.cmp(&lesser_version), Ordering::Greater);
1954 }
1955
1956 #[test]
1957 fn comparison_with_different_patch_version() {
1958 let lesser_version = Version::new(
1959 1,
1960 2,
1961 34,
1962 vec![AlphaNumeric("abc".into()), Numeric(123)],
1963 vec![],
1964 );
1965 let greater_version = Version::new(
1966 1,
1967 2,
1968 56,
1969 vec![AlphaNumeric("abc".into()), Numeric(123)],
1970 vec![],
1971 );
1972 assert_eq!(lesser_version.cmp(&greater_version), Ordering::Less);
1973 assert_eq!(greater_version.cmp(&lesser_version), Ordering::Greater);
1974 }
1975
1976 #[test]
1977 fn comparison_with_different_pre_release_version() {
1981 let v1_alpha = Version::new(1, 0, 0, vec![AlphaNumeric("alpha".into())], vec![]);
1982 let v1_alpha1 = Version::new(
1983 1,
1984 0,
1985 0,
1986 vec![AlphaNumeric("alpha".into()), Numeric(1)],
1987 vec![],
1988 );
1989 assert_eq!(v1_alpha.cmp(&v1_alpha1), Ordering::Less);
1990 let v1_alpha_beta = Version::new(
1991 1,
1992 0,
1993 0,
1994 vec![AlphaNumeric("alpha".into()), AlphaNumeric("beta".into())],
1995 vec![],
1996 );
1997 assert_eq!(v1_alpha1.cmp(&v1_alpha_beta), Ordering::Less);
1998 let v1_beta = Version::new(1, 0, 0, vec![AlphaNumeric("beta".into())], vec![]);
1999 assert_eq!(v1_alpha_beta.cmp(&v1_beta), Ordering::Less);
2000 let v1_beta2 = Version::new(
2001 1,
2002 0,
2003 0,
2004 vec![AlphaNumeric("beta".into()), Numeric(2)],
2005 vec![],
2006 );
2007 assert_eq!(v1_beta.cmp(&v1_beta2), Ordering::Less);
2008 let v1_beta11 = Version::new(
2009 1,
2010 0,
2011 0,
2012 vec![AlphaNumeric("beta".into()), Numeric(11)],
2013 vec![],
2014 );
2015 assert_eq!(v1_beta2.cmp(&v1_beta11), Ordering::Less);
2016 let v1_rc1 = Version::new(1, 0, 0, vec![AlphaNumeric("rc".into()), Numeric(1)], vec![]);
2017 assert_eq!(v1_beta11.cmp(&v1_rc1), Ordering::Less);
2018 let v1 = Version::new(1, 0, 0, vec![], vec![]);
2019 assert_eq!(v1_rc1.cmp(&v1), Ordering::Less);
2020 }
2021
2022 #[test]
2023 fn individual_version_component_has_an_upper_bound() {
2024 let out_of_range = MAX_SAFE_INTEGER + 1;
2025 let v = Version::parse(format!("1.2.{}", out_of_range));
2026 assert_eq!(
2027 v.expect_err("Parse should have failed.").to_string(),
2028 "Integer component of semver string is larger than JavaScript's Number.MAX_SAFE_INTEGER: 900719925474100"
2029 );
2030 }
2031
2032 #[test]
2033 fn version_string_limited_to_256_characters() {
2034 let prebuild = (0..257).map(|_| "X").collect::<Vec<_>>().join("");
2035 let version_string = format!("1.1.1-{}", prebuild);
2036 let v = Version::parse(version_string.clone());
2037
2038 assert_eq!(
2039 v.expect_err("Parse should have failed").to_string(),
2040 "Semver string can't be longer than 256 characters."
2041 );
2042
2043 let ok_version = version_string[0..255].to_string();
2044 let v = Version::parse(ok_version);
2045 assert!(v.is_ok());
2046 }
2047
2048 #[test]
2049 fn version_prefixed_with_v() {
2050 let v = Version::parse("v1.2.3").unwrap();
2052 assert_eq!(v, Version::new(1, 2, 3, vec![], vec![]));
2053 }
2054
2055 #[test]
2056 fn version_prefixed_with_v_space() {
2057 let v = Version::parse("v 1.2.3").unwrap();
2059 assert_eq!(v, Version::new(1, 2, 3, vec![], vec![]));
2060 }
2061
2062 fn asset_version_diff(left: &str, right: &str, expected: &str) {
2063 let left = Version::parse(left).unwrap();
2064 let right = Version::parse(right).unwrap();
2065 let expected_diff = match expected {
2066 "major" => Some(VersionDiff::Major),
2067 "minor" => Some(VersionDiff::Minor),
2068 "patch" => Some(VersionDiff::Patch),
2069 "premajor" => Some(VersionDiff::PreMajor),
2070 "preminor" => Some(VersionDiff::PreMinor),
2071 "prepatch" => Some(VersionDiff::PrePatch),
2072 "null" => None,
2073 _ => unreachable!("unexpected version diff"),
2074 };
2075
2076 assert_eq!(
2077 left.diff(&right),
2078 expected_diff,
2079 "left: {}, right: {}",
2080 left,
2081 right
2082 );
2083 }
2084
2085 #[test]
2086 fn version_diffs() {
2087 let cases = vec![
2088 ("1.2.3", "0.2.3", "major"),
2089 ("0.2.3", "1.2.3", "major"),
2090 ("1.4.5", "0.2.3", "major"),
2091 ("1.2.3", "2.0.0-pre", "premajor"),
2092 ("2.0.0-pre", "1.2.3", "premajor"),
2093 ("1.2.3", "1.3.3", "minor"),
2094 ("1.0.1", "1.1.0-pre", "preminor"),
2095 ("1.2.3", "1.2.4", "patch"),
2096 ("1.2.3", "1.2.4-pre", "prepatch"),
2097 ("1.0.0", "1.0.0", "null"),
2098 ("1.0.0-1", "1.0.0-1", "null"),
2099 ("0.0.2-1", "0.0.2", "patch"),
2100 ("0.0.2-1", "0.0.3", "patch"),
2101 ("0.0.2-1", "0.1.0", "minor"),
2102 ("0.0.2-1", "1.0.0", "major"),
2103 ("0.1.0-1", "0.1.0", "minor"),
2104 ("1.0.0-1", "2.0.0-1", "premajor"),
2105 ("1.0.0-1", "1.1.0-1", "preminor"),
2106 ("1.0.0-1", "1.0.1-1", "prepatch"),
2107 ];
2108
2109 for case in cases {
2110 asset_version_diff(case.0, case.1, case.2);
2111 }
2112 }
2113
2114 #[test]
2115 fn increments_match_node_semver_fixture() {
2116 for case in load_increment_cases() {
2117 if let Some(expected) = &case.expected {
2118 let version = Version::parse(&case.version).unwrap_or_else(|e| {
2119 panic!(
2120 "expected to parse {} but failed: {}",
2121 case.version,
2122 e.to_string()
2123 )
2124 });
2125 let before = version.to_string();
2126 let build = version.build().to_vec();
2127 let incremented = version
2128 .inc(
2129 &case.release,
2130 case.identifier.as_deref(),
2131 case.identifier_base,
2132 )
2133 .unwrap_or_else(|e| {
2134 panic!(
2135 "expected {} {} to succeed but errored: {}",
2136 case.version, case.release, e
2137 )
2138 });
2139
2140 assert_eq!(
2141 version_without_build(&incremented),
2142 expected.as_str(),
2143 "incrementing {} {} {:?} {:?}",
2144 case.version,
2145 case.release,
2146 case.identifier,
2147 case.identifier_base
2148 );
2149 assert_eq!(
2150 incremented.build(),
2151 build.as_slice(),
2152 "build metadata should remain unchanged after increment"
2153 );
2154 assert_eq!(
2155 version.to_string(),
2156 before,
2157 "original version should remain unchanged"
2158 );
2159 } else if let Ok(version) = Version::parse(&case.version) {
2160 let before = version.to_string();
2161 assert!(
2162 version
2163 .inc(
2164 &case.release,
2165 case.identifier.as_deref(),
2166 case.identifier_base
2167 )
2168 .is_err(),
2169 "expected {} {} to fail",
2170 case.version,
2171 case.release
2172 );
2173 assert_eq!(
2174 version.to_string(),
2175 before,
2176 "version should stay unchanged on error"
2177 );
2178 }
2179 }
2180 }
2181
2182 #[test]
2183 fn invalid_increment_errors_match_node_semver() {
2184 let version = Version::parse("1.2.3").unwrap();
2185 let err = version
2186 .inc("prerelease", Some(""), Some(IdentifierBase::False))
2187 .unwrap_err();
2188 assert_eq!(
2189 err.to_string(),
2190 "invalid increment argument: identifier is empty"
2191 );
2192 assert_eq!(version.to_string(), "1.2.3");
2193
2194 let version = Version::parse("1.2.3-dev").unwrap();
2195 let err = version
2196 .inc("prerelease", Some("dev"), Some(IdentifierBase::False))
2197 .unwrap_err();
2198 assert_eq!(
2199 err.to_string(),
2200 "invalid increment argument: identifier already exists"
2201 );
2202 assert_eq!(version.to_string(), "1.2.3-dev");
2203
2204 let version = Version::parse("1.2.3").unwrap();
2205 let err = version
2206 .inc("prerelease", Some("invalid/preid"), None)
2207 .unwrap_err();
2208 assert_eq!(err.to_string(), "invalid identifier: invalid/preid");
2209 assert_eq!(version.to_string(), "1.2.3");
2210 }
2211}
2212
2213#[cfg(feature = "serde")]
2214#[cfg(test)]
2215mod serde_tests {
2216 use super::Identifier::*;
2217 use super::*;
2218
2219 #[test]
2220 fn version_serde() {
2221 let v = Version::new(
2222 1,
2223 2,
2224 3,
2225 vec![AlphaNumeric("abc".into()), Numeric(123)],
2226 vec![AlphaNumeric("build".into())],
2227 );
2228
2229 let serialized = serde_json::to_string(&v).unwrap();
2230 let deserialized: Version = serde_json::from_str(&serialized).unwrap();
2231
2232 assert_eq!(v, deserialized);
2233 }
2234}