//! [![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.14")] #![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::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) } } 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 { // Work around https://github.com/rust-lang/rust/issues/97933 #[cfg(all(doc, semver_rustdoc_workaround))] pub const EMPTY: Self = ""; #[cfg(not(all(doc, semver_rustdoc_workaround)))] 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 { // Work around https://github.com/rust-lang/rust/issues/97933 #[cfg(all(doc, semver_rustdoc_workaround))] pub const EMPTY: Self = ""; #[cfg(not(all(doc, semver_rustdoc_workaround)))] 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() } }