1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
#![deny(missing_docs)]
//! # version-number
//!
//! ## Synopsis
//!
//! A crate to represent and parse two- and three-component version numbers of the form `major.minor`,
//! and `major.minor.patch` respectively. These version numbers are often seen within the Rust
//! project manifests.
//!
//! ## Semver compatibility
//!
//! The version numbers accepted by this crate are a subset of semver version numbers,
//! with the exception of also allowing two component (shorthand) `major.minor` versions.
//!
//! For example, `1.0` and `1.0.0` are both accepted by this library, while the former is
//! rejected by [`semver`].
//!
//! In addition [`Version`] does not accept extra labels such as build parameters, which are
//! an extension of the [`semver`] version number itself.
//!
//! In this crate, we call a two component `major.minor` version number a [`BaseVersion`], and
//! we call a three component `major.minor.patch` version number a [`FullVersion`].
//!
//! [`semver`]: https://semver.org/spec/v2.0.0.html
//! [`Version`]: crate::Version
//! [`BaseVersion`]: crate::BaseVersion
//! [`FullVersion`]: crate::FullVersion
use std::fmt;
use std::str::FromStr;
use crate::parsers::original;
pub use parsers::{BaseVersionParser, FullVersionParser, ParserError, VersionParser};
pub use version::{BaseVersion, FullVersion};
/// This crate contains multiple parsers.
///
/// In general, it's easiest to use the well tested [`parsers::original::Parser`], which is also used
/// (currently) by [`Version::parse`].
pub mod parsers;
mod version;
/// Top level errors for version-numbers.
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// An error which specifies failure to parse a version number.
#[error("{0}")]
ParserError(#[from] ParserError),
}
/// A numbered version which is a two-component `major.minor` version number,
/// or a three-component `major.minor.patch` version number.
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub enum Version {
/// A two-component `major.minor` version.
Base(BaseVersion),
/// A three-component `major.minor.patch` version.
Full(FullVersion),
}
impl Version {
/// Parse a two- or three-component, `major.minor` or `major.minor.patch` respectively,
/// version number from a given input.
///
/// Returns a [`Error::ParserError`] if it fails to parse.
pub fn parse(input: &str) -> Result<Self, Error> {
original::Parser::from(input.as_bytes())
.parse()
.map_err(|e| Error::from(Into::<ParserError>::into(e)))
}
/// Create a new two-component `major.minor` version number.
pub fn new_base_version(major: u64, minor: u64) -> Self {
Self::Base(BaseVersion { major, minor })
}
/// Create a new three-component `major.minor.patch` version number.
pub fn new_full_version(major: u64, minor: u64, patch: u64) -> Self {
Self::Full(FullVersion {
major,
minor,
patch,
})
}
/// Returns the `major` version component.
///
/// Both the two- and three-component version number variants have a major version.
/// This is the leading component.
pub fn major(&self) -> u64 {
match self {
Self::Base(inner) => inner.major,
Self::Full(inner) => inner.major,
}
}
/// Returns the `minor` version component.
///
/// Both the two- and three-component version number variants have a minor version.
/// This is the middle component.
pub fn minor(&self) -> u64 {
match self {
Self::Base(inner) => inner.minor,
Self::Full(inner) => inner.minor,
}
}
/// Returns the `patch` version component, if any.
///
/// A three component `major.minor.patch` version will return a `Some(<version>)`,
/// while a two component `major.minor` version will return `None` instead.
///
/// If it exists, it is the last component.
pub fn patch(&self) -> Option<u64> {
match self {
Self::Base(_) => None,
Self::Full(inner) => Some(inner.patch),
}
}
/// Check of which variant `self` is.
pub fn is(&self, variant: Variant) -> bool {
match self {
Version::Base(_) => matches!(variant, Variant::Base),
Version::Full(_) => matches!(variant, Variant::Full),
}
}
}
impl FromStr for Version {
type Err = Error;
fn from_str(input: &str) -> Result<Self, Error> {
original::Parser::from_slice(input.as_bytes())
.parse()
.map_err(|e| Error::from(Into::<ParserError>::into(e)))
}
}
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Base(inner) => fmt::Display::fmt(&inner, f),
Self::Full(inner) => fmt::Display::fmt(&inner, f),
}
}
}
impl From<(u64, u64)> for Version {
fn from(tuple: (u64, u64)) -> Self {
Self::Base(BaseVersion::from(tuple))
}
}
impl From<(u64, u64, u64)> for Version {
fn from(tuple: (u64, u64, u64)) -> Self {
Self::Full(FullVersion::from(tuple))
}
}
/// Type used to indicate which variant of a [`Version`] is used.
/// The options are [`Base`] for [`Version::Base`], and [`Full`] for [`Version::Full`].
///
/// [`Version`]: crate::Version
/// [`Base`]: crate::Variant::Base
/// [`Version::Base`]: crate::Version::Base
/// [`Full`]: crate::Variant::Full
/// [`Version::Full`]: crate::Version::Full
#[derive(Copy, Clone, Debug)]
pub enum Variant {
/// Indicates a [`Version::Base`] is used.
///
/// [`Version::Base`]: crate::Version::Base
Base,
/// Indicates a [`Version::Full`] is used.
///
/// [`Version::Full`]: crate::Version::Full
Full,
}
#[cfg(test)]
mod tests {
use crate::{BaseVersion, FullVersion, Variant, Version};
#[test]
fn is_base_variant() {
let version = Version::Base(BaseVersion::new(0, 0));
assert!(version.is(Variant::Base));
assert!(!version.is(Variant::Full));
}
#[test]
fn is_full_variant() {
let version = Version::Full(FullVersion::new(0, 0, 0));
assert!(version.is(Variant::Full));
assert!(!version.is(Variant::Base));
}
}