281 lines
8.0 KiB
Rust
281 lines
8.0 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 PartialOrd for Version {
|
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
|
if self.arch == other.arch {
|
|
Some(u128::from(*self).cmp(&u128::from(*other)))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::{num::NonZeroU16, str::FromStr};
|
|
|
|
#[test]
|
|
fn from_str() {
|
|
let mut version: Version = "2.4.1_alpha2-aarch64".parse().unwrap();
|
|
assert_eq!(
|
|
version,
|
|
Version {
|
|
kind: Kind::SemVer {
|
|
major: 2,
|
|
minor: 4,
|
|
patch: 1
|
|
},
|
|
pre: PreRelease::Alpha(Some(NonZeroU16::new(2).unwrap())),
|
|
arch: Arch::Arm64,
|
|
}
|
|
);
|
|
version = "6.4-i486".parse().unwrap();
|
|
assert_eq!(
|
|
version,
|
|
Version {
|
|
kind: Kind::Rapid { major: 6, minor: 4 },
|
|
pre: PreRelease::None,
|
|
arch: Arch::X86,
|
|
}
|
|
);
|
|
version = "3.14.6_git_r2d2xxx.1705881493-amd64".parse().unwrap();
|
|
assert_eq!(
|
|
version,
|
|
Version {
|
|
kind: Kind::SemVer {
|
|
major: 3,
|
|
minor: 14,
|
|
patch: 6
|
|
},
|
|
pre: PreRelease::Git {
|
|
hash: ['r', '2', 'd', '2', 'x', 'x', 'x'],
|
|
datetime: epoch::DateTime {
|
|
year: epoch::prelude::Year::Leap(2024),
|
|
month: epoch::prelude::Month::Janurary,
|
|
day: 21,
|
|
hour: 23,
|
|
minute: 58,
|
|
second: 13,
|
|
zone: epoch::prelude::TimeZone::Utc
|
|
}
|
|
},
|
|
arch: Arch::X86_64,
|
|
}
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn cmp() {
|
|
let astr = "3.14.0-x86_64";
|
|
let bstr = "3.14.0_alpha1-x86_64";
|
|
let cstr = "3.14_alpha1-amd64";
|
|
let dstr = "3.14.0_beta3-x86_64";
|
|
let estr = "3.14.0_git_c3poxxx.1705881493-x86_64";
|
|
let fstr = "3.14.0_git_r2d2xxx.1705900000-x86_64";
|
|
assert!(Version::from_str(astr).unwrap() > Version::from_str(bstr).unwrap());
|
|
assert!(Version::from_str(cstr).unwrap() == Version::from_str(bstr).unwrap());
|
|
assert!(Version::from_str(cstr).unwrap() < Version::from_str(dstr).unwrap());
|
|
assert!(Version::from_str(dstr).unwrap() > Version::from_str(estr).unwrap());
|
|
assert!(Version::from_str(fstr).unwrap() > Version::from_str(estr).unwrap());
|
|
}
|
|
}
|