//! [![github]](https://github.com/dtolnay/semver) [![crates-io]](https://crates.io/crates/semver) [![docs-rs]](https://docs.rs/semver)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
//!
//!
//!
//! A parser and evaluator for Cargo's flavor of Semantic Versioning.
//!
//! Semantic Versioning (see ) is a guideline for how
//! version numbers are assigned and incremented. It is widely followed within
//! the Cargo/crates.io ecosystem for Rust.
//!
//!
//!
//! # Example
//!
//! ```
//! use semver::{BuildMetadata, Prerelease, Version, VersionReq};
//!
//! fn main() {
//! let req = VersionReq::parse(">=1.2.3, <1.8.0").unwrap();
//!
//! // Check whether this requirement matches version 1.2.3-alpha.1 (no)
//! let version = Version {
//! major: 1,
//! minor: 2,
//! patch: 3,
//! pre: Prerelease::new("alpha.1").unwrap(),
//! build: BuildMetadata::EMPTY,
//! };
//! assert!(!req.matches(&version));
//!
//! // Check whether it matches 1.3.0 (yes it does)
//! let version = Version::parse("1.3.0").unwrap();
//! assert!(req.matches(&version));
//! }
//! ```
//!
//!
//!
//! # Scope of this crate
//!
//! Besides Cargo, several other package ecosystems and package managers for
//! other languages also use SemVer: RubyGems/Bundler for Ruby, npm for
//! JavaScript, Composer for PHP, CocoaPods for Objective-C...
//!
//! The `semver` crate is specifically intended to implement Cargo's
//! interpretation of Semantic Versioning.
//!
//! Where the various tools differ in their interpretation or implementation of
//! the spec, this crate follows the implementation choices made by Cargo. If
//! you are operating on version numbers from some other package ecosystem, you
//! will want to use a different semver library which is appropriate to that
//! ecosystem.
//!
//! The extent of Cargo's SemVer support is documented in the *[Specifying
//! Dependencies]* chapter of the Cargo reference.
//!
//! [Specifying Dependencies]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
#![doc(html_root_url = "https://docs.rs/semver/1.0.20")]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
#![cfg_attr(all(not(feature = "std"), not(no_alloc_crate)), no_std)]
#![cfg_attr(not(no_unsafe_op_in_unsafe_fn_lint), deny(unsafe_op_in_unsafe_fn))]
#![cfg_attr(no_unsafe_op_in_unsafe_fn_lint, allow(unused_unsafe))]
#![cfg_attr(no_str_strip_prefix, allow(unstable_name_collisions))]
#![allow(
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::doc_markdown,
clippy::items_after_statements,
clippy::manual_map,
clippy::match_bool,
clippy::missing_errors_doc,
clippy::must_use_candidate,
clippy::needless_doctest_main,
clippy::option_if_let_else,
clippy::ptr_as_ptr,
clippy::redundant_else,
clippy::semicolon_if_nothing_returned, // https://github.com/rust-lang/rust-clippy/issues/7324
clippy::similar_names,
clippy::unnested_or_patterns,
clippy::unseparated_literal_suffix,
clippy::wildcard_imports
)]
#[cfg(not(no_alloc_crate))]
extern crate alloc;
mod backport;
mod display;
mod error;
mod eval;
mod identifier;
mod impls;
mod parse;
#[cfg(feature = "serde")]
mod serde;
use crate::alloc::vec::Vec;
use crate::identifier::Identifier;
use core::cmp::Ordering;
use core::str::FromStr;
#[allow(unused_imports)]
use crate::backport::*;
pub use crate::parse::Error;
/// **SemVer version** as defined by .
///
/// # Syntax
///
/// - The major, minor, and patch numbers may be any integer 0 through u64::MAX.
/// When representing a SemVer version as a string, each number is written as
/// a base 10 integer. For example, `1.0.119`.
///
/// - Leading zeros are forbidden in those positions. For example `1.01.00` is
/// invalid as a SemVer version.
///
/// - The pre-release identifier, if present, must conform to the syntax
/// documented for [`Prerelease`].
///
/// - The build metadata, if present, must conform to the syntax documented for
/// [`BuildMetadata`].
///
/// - Whitespace is not allowed anywhere in the version.
///
/// # Total ordering
///
/// Given any two SemVer versions, one is less than, greater than, or equal to
/// the other. Versions may be compared against one another using Rust's usual
/// comparison operators.
///
/// - The major, minor, and patch number are compared numerically from left to
/// right, lexicographically ordered as a 3-tuple of integers. So for example
/// version `1.5.0` is less than version `1.19.0`, despite the fact that
/// "1.19.0" < "1.5.0" as ASCIIbetically compared strings and 1.19 < 1.5
/// as real numbers.
///
/// - When major, minor, and patch are equal, a pre-release version is
/// considered less than the ordinary release: version `1.0.0-alpha.1` is
/// less than version `1.0.0`.
///
/// - Two pre-releases of the same major, minor, patch are compared by
/// lexicographic ordering of dot-separated components of the pre-release
/// string.
///
/// - Identifiers consisting of only digits are compared
/// numerically: `1.0.0-pre.8` is less than `1.0.0-pre.12`.
///
/// - Identifiers that contain a letter or hyphen are compared in ASCII sort
/// order: `1.0.0-pre12` is less than `1.0.0-pre8`.
///
/// - Any numeric identifier is always less than any non-numeric
/// identifier: `1.0.0-pre.1` is less than `1.0.0-pre.x`.
///
/// Example: `1.0.0-alpha` < `1.0.0-alpha.1` < `1.0.0-alpha.beta` < `1.0.0-beta` < `1.0.0-beta.2` < `1.0.0-beta.11` < `1.0.0-rc.1` < `1.0.0`
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Version {
pub major: u64,
pub minor: u64,
pub patch: u64,
pub pre: Prerelease,
pub build: BuildMetadata,
}
/// **SemVer version requirement** describing the intersection of some version
/// comparators, such as `>=1.2.3, <1.8`.
///
/// # Syntax
///
/// - Either `*` (meaning "any"), or one or more comma-separated comparators.
///
/// - A [`Comparator`] is an operator ([`Op`]) and a partial version, separated
/// by optional whitespace. For example `>=1.0.0` or `>=1.0`.
///
/// - Build metadata is syntactically permitted on the partial versions, but is
/// completely ignored, as it's never relevant to whether any comparator
/// matches a particular version.
///
/// - Whitespace is permitted around commas and around operators. Whitespace is
/// not permitted within a partial version, i.e. anywhere between the major
/// version number and its minor, patch, pre-release, or build metadata.
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[cfg_attr(no_const_vec_new, derive(Default))]
pub struct VersionReq {
pub comparators: Vec,
}
/// A pair of comparison operator and partial version, such as `>=1.2`. Forms
/// one piece of a VersionReq.
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct Comparator {
pub op: Op,
pub major: u64,
pub minor: Option,
/// Patch is only allowed if minor is Some.
pub patch: Option,
/// Non-empty pre-release is only allowed if patch is Some.
pub pre: Prerelease,
}
/// SemVer comparison operator: `=`, `>`, `>=`, `<`, `<=`, `~`, `^`, `*`.
///
/// # Op::Exact
/// - **`=I.J.K`** — exactly the version I.J.K
/// - **`=I.J`** — equivalent to `>=I.J.0, =I.0.0, <(I+1).0.0`
///
/// # Op::Greater
/// - **`>I.J.K`**
/// - **`>I.J`** — equivalent to `>=I.(J+1).0`
/// - **`>I`** — equivalent to `>=(I+1).0.0`
///
/// # Op::GreaterEq
/// - **`>=I.J.K`**
/// - **`>=I.J`** — equivalent to `>=I.J.0`
/// - **`>=I`** — equivalent to `>=I.0.0`
///
/// # Op::Less
/// - **`=I.J.K, 0) — equivalent to `>=I.J.K, <(I+1).0.0`
/// - **`^0.J.K`** (for J\>0) — equivalent to `>=0.J.K, <0.(J+1).0`
/// - **`^0.0.K`** — equivalent to `=0.0.K`
/// - **`^I.J`** (for I\>0 or J\>0) — equivalent to `^I.J.0`
/// - **`^0.0`** — equivalent to `=0.0`
/// - **`^I`** — equivalent to `=I`
///
/// # Op::Wildcard
/// - **`I.J.*`** — equivalent to `=I.J`
/// - **`I.*`** or **`I.*.*`** — equivalent to `=I`
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[cfg_attr(not(no_non_exhaustive), non_exhaustive)]
pub enum Op {
Exact,
Greater,
GreaterEq,
Less,
LessEq,
Tilde,
Caret,
Wildcard,
#[cfg(no_non_exhaustive)] // rustc <1.40
#[doc(hidden)]
__NonExhaustive,
}
/// Optional pre-release identifier on a version string. This comes after `-` in
/// a SemVer version, like `1.0.0-alpha.1`
///
/// # Examples
///
/// Some real world pre-release idioms drawn from crates.io:
///
/// - **[mio]** 0.7.0-alpha.1
— the most common style
/// for numbering pre-releases.
///
/// - **[pest]** 1.0.0-beta.8
, 1.0.0-rc.0
/// — this crate makes a distinction between betas and release
/// candidates.
///
/// - **[sassers]** 0.11.0-shitshow
— ???.
///
/// - **[atomic-utils]** 0.0.0-reserved
— a squatted
/// crate name.
///
/// [mio]: https://crates.io/crates/mio
/// [pest]: https://crates.io/crates/pest
/// [atomic-utils]: https://crates.io/crates/atomic-utils
/// [sassers]: https://crates.io/crates/sassers
///
/// *Tip:* Be aware that if you are planning to number your own pre-releases,
/// you should prefer to separate the numeric part from any non-numeric
/// identifiers by using a dot in between. That is, prefer pre-releases
/// `alpha.1`, `alpha.2`, etc rather than `alpha1`, `alpha2` etc. The SemVer
/// spec's rule for pre-release precedence has special treatment of numeric
/// components in the pre-release string, but only if there are no non-digit
/// characters in the same dot-separated component. So you'd have `alpha.2` <
/// `alpha.11` as intended, but `alpha11` < `alpha2`.
///
/// # Syntax
///
/// Pre-release strings are a series of dot separated identifiers immediately
/// following the patch version. Identifiers must comprise only ASCII
/// alphanumerics and hyphens: `0-9`, `A-Z`, `a-z`, `-`. Identifiers must not be
/// empty. Numeric identifiers must not include leading zeros.
///
/// # Total ordering
///
/// Pre-releases have a total order defined by the SemVer spec. It uses
/// lexicographic ordering of dot-separated components. Identifiers consisting
/// of only digits are compared numerically. Otherwise, identifiers are compared
/// in ASCII sort order. Any numeric identifier is always less than any
/// non-numeric identifier.
///
/// Example: `alpha` < `alpha.85` < `alpha.90` < `alpha.200` < `alpha.0a` < `alpha.1a0` < `alpha.a` < `beta`
#[derive(Default, Clone, Eq, PartialEq, Hash)]
pub struct Prerelease {
identifier: Identifier,
}
/// Optional build metadata identifier. This comes after `+` in a SemVer
/// version, as in `0.8.1+zstd.1.5.0`.
///
/// # Examples
///
/// Some real world build metadata idioms drawn from crates.io:
///
/// - **[libgit2-sys]** 0.12.20+1.1.0
— for this
/// crate, the build metadata indicates the version of the C libgit2 library
/// that the Rust crate is built against.
///
/// - **[mashup]** 0.1.13+deprecated
— just the word
/// "deprecated" for a crate that has been superseded by another. Eventually
/// people will take notice of this in Cargo's build output where it lists the
/// crates being compiled.
///
/// - **[google-bigquery2]** 2.0.4+20210327
— this
/// library is automatically generated from an official API schema, and the
/// build metadata indicates the date on which that schema was last captured.
///
/// - **[fbthrift-git]** 0.0.6+c7fcc0e
— this crate is
/// published from snapshots of a big company monorepo. In monorepo
/// development, there is no concept of versions, and all downstream code is
/// just updated atomically in the same commit that breaking changes to a
/// library are landed. Therefore for crates.io purposes, every published
/// version must be assumed to be incompatible with the previous. The build
/// metadata provides the source control hash of the snapshotted code.
///
/// [libgit2-sys]: https://crates.io/crates/libgit2-sys
/// [mashup]: https://crates.io/crates/mashup
/// [google-bigquery2]: https://crates.io/crates/google-bigquery2
/// [fbthrift-git]: https://crates.io/crates/fbthrift-git
///
/// # Syntax
///
/// Build metadata is a series of dot separated identifiers immediately
/// following the patch or pre-release version. Identifiers must comprise only
/// ASCII alphanumerics and hyphens: `0-9`, `A-Z`, `a-z`, `-`. Identifiers must
/// not be empty. Leading zeros *are* allowed, unlike any other place in the
/// SemVer grammar.
///
/// # Total ordering
///
/// Build metadata is ignored in evaluating `VersionReq`; it plays no role in
/// whether a `Version` matches any one of the comparison operators.
///
/// However for comparing build metadatas among one another, they do have a
/// total order which is determined by lexicographic ordering of dot-separated
/// components. Identifiers consisting of only digits are compared numerically.
/// Otherwise, identifiers are compared in ASCII sort order. Any numeric
/// identifier is always less than any non-numeric identifier.
///
/// Example: `demo` < `demo.85` < `demo.90` < `demo.090` < `demo.200` < `demo.1a0` < `demo.a` < `memo`
#[derive(Default, Clone, Eq, PartialEq, Hash)]
pub struct BuildMetadata {
identifier: Identifier,
}
impl Version {
/// Create `Version` with an empty pre-release and build metadata.
///
/// Equivalent to:
///
/// ```
/// # use semver::{BuildMetadata, Prerelease, Version};
/// #
/// # const fn new(major: u64, minor: u64, patch: u64) -> Version {
/// Version {
/// major,
/// minor,
/// patch,
/// pre: Prerelease::EMPTY,
/// build: BuildMetadata::EMPTY,
/// }
/// # }
/// ```
pub const fn new(major: u64, minor: u64, patch: u64) -> Self {
Version {
major,
minor,
patch,
pre: Prerelease::EMPTY,
build: BuildMetadata::EMPTY,
}
}
/// Create `Version` by parsing from string representation.
///
/// # Errors
///
/// Possible reasons for the parse to fail include:
///
/// - `1.0` — too few numeric components. A SemVer version must have
/// exactly three. If you are looking at something that has fewer than
/// three numbers in it, it's possible it is a `VersionReq` instead (with
/// an implicit default `^` comparison operator).
///
/// - `1.0.01` — a numeric component has a leading zero.
///
/// - `1.0.unknown` — unexpected character in one of the components.
///
/// - `1.0.0-` or `1.0.0+` — the pre-release or build metadata are
/// indicated present but empty.
///
/// - `1.0.0-alpha_123` — pre-release or build metadata have something
/// outside the allowed characters, which are `0-9`, `A-Z`, `a-z`, `-`,
/// and `.` (dot).
///
/// - `23456789999999999999.0.0` — overflow of a u64.
pub fn parse(text: &str) -> Result {
Version::from_str(text)
}
/// Compare the major, minor, patch, and pre-release value of two versions,
/// disregarding build metadata. Versions that differ only in build metadata
/// are considered equal. This comparison is what the SemVer spec refers to
/// as "precedence".
///
/// # Example
///
/// ```
/// use semver::Version;
///
/// let mut versions = [
/// "1.20.0+c144a98".parse::().unwrap(),
/// "1.20.0".parse().unwrap(),
/// "1.0.0".parse().unwrap(),
/// "1.0.0-alpha".parse().unwrap(),
/// "1.20.0+bc17664".parse().unwrap(),
/// ];
///
/// // This is a stable sort, so it preserves the relative order of equal
/// // elements. The three 1.20.0 versions differ only in build metadata so
/// // they are not reordered relative to one another.
/// versions.sort_by(Version::cmp_precedence);
/// assert_eq!(versions, [
/// "1.0.0-alpha".parse().unwrap(),
/// "1.0.0".parse().unwrap(),
/// "1.20.0+c144a98".parse().unwrap(),
/// "1.20.0".parse().unwrap(),
/// "1.20.0+bc17664".parse().unwrap(),
/// ]);
///
/// // Totally order the versions, including comparing the build metadata.
/// versions.sort();
/// assert_eq!(versions, [
/// "1.0.0-alpha".parse().unwrap(),
/// "1.0.0".parse().unwrap(),
/// "1.20.0".parse().unwrap(),
/// "1.20.0+bc17664".parse().unwrap(),
/// "1.20.0+c144a98".parse().unwrap(),
/// ]);
/// ```
pub fn cmp_precedence(&self, other: &Self) -> Ordering {
Ord::cmp(
&(self.major, self.minor, self.patch, &self.pre),
&(other.major, other.minor, other.patch, &other.pre),
)
}
}
impl VersionReq {
/// A `VersionReq` with no constraint on the version numbers it matches.
/// Equivalent to `VersionReq::parse("*").unwrap()`.
///
/// In terms of comparators this is equivalent to `>=0.0.0`.
///
/// Counterintuitively a `*` VersionReq does not match every possible
/// version number. In particular, in order for *any* `VersionReq` to match
/// a pre-release version, the `VersionReq` must contain at least one
/// `Comparator` that has an explicit major, minor, and patch version
/// identical to the pre-release being matched, and that has a nonempty
/// pre-release component. Since `*` is not written with an explicit major,
/// minor, and patch version, and does not contain a nonempty pre-release
/// component, it does not match any pre-release versions.
#[cfg(not(no_const_vec_new))] // rustc <1.39
pub const STAR: Self = VersionReq {
comparators: Vec::new(),
};
/// Create `VersionReq` by parsing from string representation.
///
/// # Errors
///
/// Possible reasons for the parse to fail include:
///
/// - `>a.b` — unexpected characters in the partial version.
///
/// - `@1.0.0` — unrecognized comparison operator.
///
/// - `^1.0.0, ` — unexpected end of input.
///
/// - `>=1.0 <2.0` — missing comma between comparators.
///
/// - `*.*` — unsupported wildcard syntax.
pub fn parse(text: &str) -> Result {
VersionReq::from_str(text)
}
/// Evaluate whether the given `Version` satisfies the version requirement
/// described by `self`.
pub fn matches(&self, version: &Version) -> bool {
eval::matches_req(self, version)
}
}
/// The default VersionReq is the same as [`VersionReq::STAR`].
#[cfg(not(no_const_vec_new))]
impl Default for VersionReq {
fn default() -> Self {
VersionReq::STAR
}
}
impl Comparator {
pub fn parse(text: &str) -> Result {
Comparator::from_str(text)
}
pub fn matches(&self, version: &Version) -> bool {
eval::matches_comparator(self, version)
}
}
impl Prerelease {
pub const EMPTY: Self = Prerelease {
identifier: Identifier::empty(),
};
pub fn new(text: &str) -> Result {
Prerelease::from_str(text)
}
pub fn as_str(&self) -> &str {
self.identifier.as_str()
}
pub fn is_empty(&self) -> bool {
self.identifier.is_empty()
}
}
impl BuildMetadata {
pub const EMPTY: Self = BuildMetadata {
identifier: Identifier::empty(),
};
pub fn new(text: &str) -> Result {
BuildMetadata::from_str(text)
}
pub fn as_str(&self) -> &str {
self.identifier.as_str()
}
pub fn is_empty(&self) -> bool {
self.identifier.is_empty()
}
}