use core::fmt::{self, Display, Formatter};
use core::num::ParseIntError;
use core::str::FromStr;
use crate::date::Date;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct StableVersionSpec {
pub major: u32,
pub minor: u32,
pub patch: Option<u32>,
}
impl StableVersionSpec {
#[inline]
pub fn minor(major: u32, minor: u32) -> Self {
check_major_version(major);
StableVersionSpec {
major,
minor,
patch: None,
}
}
#[inline]
pub fn patch(major: u32, minor: u32, patch: u32) -> Self {
check_major_version(major);
StableVersionSpec {
major,
minor,
patch: Some(patch),
}
}
#[inline]
pub fn to_version(&self) -> RustVersion {
RustVersion::stable(self.major, self.minor, self.patch.unwrap_or(0))
}
}
impl FromStr for StableVersionSpec {
type Err = StableVersionParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut iter = s.split('.');
let major = iter
.next()
.ok_or(StableVersionParseError::BadNumberParts)?
.parse::<u32>()?;
let minor = iter
.next()
.ok_or(StableVersionParseError::BadNumberParts)?
.parse::<u32>()?;
let patch = match iter.next() {
Some(patch_text) => Some(patch_text.parse::<u32>()?),
None => None,
};
if iter.next().is_some() {
return Err(StableVersionParseError::BadNumberParts);
}
if major != 1 {
return Err(StableVersionParseError::InvalidMajorVersion);
}
Ok(StableVersionSpec {
major,
minor,
patch,
})
}
}
#[derive(Clone, Debug)]
pub enum StableVersionParseError {
#[doc(hidden)]
InvalidNumber(ParseIntError),
#[doc(hidden)]
BadNumberParts,
#[doc(hidden)]
InvalidMajorVersion,
}
impl From<ParseIntError> for StableVersionParseError {
#[inline]
fn from(cause: ParseIntError) -> Self {
StableVersionParseError::InvalidNumber(cause)
}
}
impl Display for StableVersionSpec {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}", self.major, self.minor)?;
if let Some(patch) = self.patch {
write!(f, ".{}", patch)?;
}
Ok(())
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct RustVersion {
pub major: u32,
pub minor: u32,
pub patch: u32,
pub channel: Channel,
}
impl RustVersion {
#[inline]
pub fn stable(major: u32, minor: u32, patch: u32) -> RustVersion {
check_major_version(major);
RustVersion {
major,
minor,
patch,
channel: Channel::Stable,
}
}
#[inline]
pub fn is_since_minor_version(&self, major: u32, minor: u32) -> bool {
self.is_since_stable(StableVersionSpec::minor(major, minor))
}
#[inline]
pub fn is_since_patch_version(&self, major: u32, minor: u32, patch: u32) -> bool {
self.is_since_stable(StableVersionSpec::patch(major, minor, patch))
}
#[inline]
pub fn is_since_stable(&self, spec: StableVersionSpec) -> bool {
self.major > spec.major
|| (self.major == spec.major
&& (self.minor > spec.minor
|| (self.minor == spec.minor
&& match spec.patch {
None => true, Some(patch_spec) => self.patch >= patch_spec,
})))
}
#[inline]
pub fn is_before_stable(&self, spec: StableVersionSpec) -> bool {
!self.is_since_stable(spec)
}
#[inline]
pub fn is_before_minor_version(&self, major: u32, minor: u32) -> bool {
self.is_before_stable(StableVersionSpec::minor(major, minor))
}
#[inline]
pub fn is_before_patch_version(&self, major: u32, minor: u32, patch: u32) -> bool {
self.is_before_stable(StableVersionSpec::patch(major, minor, patch))
}
#[inline]
pub fn is_since_nightly(&self, start: Date) -> bool {
match self.channel {
Channel::Nightly { date } => date.is_since(start),
Channel::Stable | Channel::Beta => false, Channel::Development => true, Channel::__NonExhaustive => unreachable!(),
}
}
#[inline]
pub fn is_before_nightly(&self, start: Date) -> bool {
match self.channel {
Channel::Nightly { date } => date <= start,
Channel::Stable | Channel::Beta => false, Channel::Development => true, Channel::__NonExhaustive => unreachable!(),
}
}
#[inline]
pub fn is_nightly(&self) -> bool {
self.channel.is_nightly()
}
#[inline]
pub fn is_stable(&self) -> bool {
self.channel.is_stable()
}
#[inline]
pub fn is_beta(&self) -> bool {
self.channel.is_beta()
}
#[inline]
pub fn is_development(&self) -> bool {
self.channel.is_development()
}
}
impl From<StableVersionSpec> for RustVersion {
#[inline]
fn from(value: StableVersionSpec) -> Self {
value.to_version()
}
}
impl Display for RustVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?;
match self.channel {
Channel::Stable => Ok(()), Channel::Beta => f.write_str("-beta"),
Channel::Nightly { ref date } => {
write!(f, "-nightly ({})", date)
}
Channel::Development => f.write_str("-dev"),
Channel::__NonExhaustive => unreachable!(),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Channel {
Stable,
Beta,
Nightly {
date: Date,
},
Development,
#[doc(hidden)]
__NonExhaustive,
}
impl Channel {
#[inline]
pub fn is_nightly(&self) -> bool {
match *self {
Channel::Nightly { .. } => true,
_ => false,
}
}
#[inline]
pub fn is_stable(&self) -> bool {
match *self {
Channel::Stable => true,
_ => false,
}
}
#[inline]
pub fn is_beta(&self) -> bool {
match *self {
Channel::Beta => true,
_ => false,
}
}
#[inline]
pub fn is_development(&self) -> bool {
match *self {
Channel::Development => true,
_ => false,
}
}
}
#[inline]
fn check_major_version(major: u32) {
assert_eq!(major, 1, "Major version must be 1.*");
}
#[cfg(test)]
mod test {
use super::{RustVersion, StableVersionSpec};
fn versions() -> Vec<(RustVersion, RustVersion)> {
vec![
(RustVersion::stable(1, 7, 8), RustVersion::stable(1, 89, 0)),
(RustVersion::stable(1, 18, 0), RustVersion::stable(1, 80, 3)),
]
}
#[cfg(test)]
impl RustVersion {
#[inline]
pub(crate) fn to_spec(&self) -> StableVersionSpec {
StableVersionSpec::patch(self.major, self.minor, self.patch)
}
}
#[test]
fn test_before_after() {
for (before, after) in versions() {
assert!(
before.is_before_stable(after.to_spec()),
"{} & {}",
before,
after
);
assert!(
after.is_since_stable(before.to_spec()),
"{} & {}",
before,
after
);
}
}
}