version-rs/src/version.rs

216 lines
5.7 KiB
Rust

use {
crate::{arch::Arch, error::Error, prerelease::PreRelease, MAX_U12},
serde::{Deserialize, Serialize},
std::{cmp, fmt, str},
};
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum Kind {
Simple {
major: u16,
},
Rapid {
major: u16,
minor: u16,
},
SemVer {
major: u16,
minor: u16,
patch: u16,
},
Extended {
major: u16,
minor: u16,
patch: u16,
build: u16,
},
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct Version {
pub kind: Kind,
pub pre: PreRelease,
pub arch: Arch,
}
impl fmt::Display for Kind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Simple { major } => write!(f, "{major}"),
Self::Rapid { major, minor } => write!(f, "{major}.{minor}"),
Self::SemVer {
major,
minor,
patch,
} => write!(f, "{major}.{minor}.{patch}"),
Self::Extended {
major,
minor,
patch,
build,
} => write!(f, "{major}.{minor}.{patch}.{build}"),
}
}
}
impl From<Kind> for u128 {
fn from(value: Kind) -> Self {
match value {
Kind::Simple { major } => u128::from(major) << (64 + 52),
Kind::Rapid { major, minor } => {
let major = u64::from(major) << 52;
let minor = u64::from(minor) << 40;
u128::from(major | minor) << 64
}
Kind::SemVer {
major,
minor,
patch,
} => {
let major = u64::from(major) << 52;
let minor = u64::from(minor) << 40;
let patch = u64::from(patch) << 28;
u128::from(major | minor | patch) << 64
}
Kind::Extended {
major,
minor,
patch,
build,
} => {
let major = u64::from(major) << 52;
let minor = u64::from(minor) << 40;
let patch = u64::from(patch) << 28;
let build = u64::from(build) << 16;
u128::from(major | minor | patch | build) << 64
}
}
}
}
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}-{}", self.kind, self.pre, self.arch)
}
}
impl From<Version> for u128 {
fn from(value: Version) -> Self {
u128::from(value.kind) | u128::from(value.pre)
}
}
impl str::FromStr for Version {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let Some((s, arch)) = s.split_once('-') else {
return Err(Error::ParseArch);
};
let arch: Arch = arch.parse()?;
let (s, pre) = match s.split_once('_') {
Some((a, b)) => (a, b.parse::<PreRelease>()?),
None => (s, PreRelease::None),
};
let mut split = s.split('.');
let Some(Ok(major)) = split.next().map(|m| m.parse::<u16>()) else {
return Err(Error::ParseSemver);
};
if major > MAX_U12 {
return Err(Error::Range);
};
let minor: u16 = match split.next().map(|m| m.parse()) {
Some(Ok(m)) => {
if m <= MAX_U12 {
m
} else {
return Err(Error::Range);
}
}
Some(Err(e)) => return Err(e.into()),
None => {
return Ok(Self {
kind: Kind::Simple { major },
pre,
arch,
})
}
};
let patch: u16 = match split.next().map(|p| p.parse()) {
Some(Ok(p)) => {
if p < MAX_U12 {
p
} else {
return Err(Error::Range);
}
}
Some(Err(e)) => return Err(e.into()),
None => {
return Ok(Self {
kind: Kind::Rapid { major, minor },
pre,
arch,
})
}
};
let build: u16 = match split.next().map(|b| b.parse()) {
Some(Ok(b)) => {
if b <= MAX_U12 {
b
} else {
return Err(Error::Range);
}
}
Some(Err(e)) => return Err(e.into()),
None => {
return Ok(Self {
kind: Kind::SemVer {
major,
minor,
patch,
},
pre,
arch,
})
}
};
match split.next() {
Some(_) => Err(Error::ParseVersion),
None => Ok(Self {
kind: Kind::Extended {
major,
minor,
patch,
build,
},
pre,
arch,
}),
}
}
}
impl PartialEq for Version {
fn eq(&self, other: &Self) -> bool {
u128::from(*self) == u128::from(*other) && self.arch == other.arch
}
}
impl Eq for Version {}
impl Ord for Version {
fn cmp(&self, other: &Self) -> cmp::Ordering {
u128::from(*self).cmp(&u128::from(*other))
}
}
impl PartialOrd for Version {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
if self.arch == other.arch {
Some(self.cmp(other))
} else {
None
}
}
}