216 lines
5.7 KiB
Rust
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
|
|
}
|
|
}
|
|
}
|