307 lines
7.9 KiB
Rust
307 lines
7.9 KiB
Rust
use {
|
|
crate::{
|
|
error::Error, gitrev::GitRev, prerelease::PreRelease, rapid::Rapid, semver::SemVer,
|
|
simple::Simple, MAX_U12,
|
|
},
|
|
serde::{Deserialize, Serialize},
|
|
std::{cmp::Ordering, fmt, str::FromStr},
|
|
};
|
|
|
|
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
|
pub struct Extended {
|
|
pub major: u16,
|
|
pub minor: u16,
|
|
pub patch: u16,
|
|
pub build: u16,
|
|
pub pre: PreRelease,
|
|
}
|
|
|
|
impl fmt::Display for Extended {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(
|
|
f,
|
|
"{}.{}.{}.{}",
|
|
self.major, self.minor, self.patch, self.build
|
|
)?;
|
|
match self.pre {
|
|
PreRelease::None => Ok(()),
|
|
p => write!(f, "_{p}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FromStr for Extended {
|
|
type Err = Error;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let (s, pre) = match s.split_once('_') {
|
|
Some((a, b)) => (a, b.parse::<PreRelease>()?),
|
|
None => (s, PreRelease::None),
|
|
};
|
|
let split = s.split('.').collect::<Vec<_>>();
|
|
match split.len() {
|
|
4 => {
|
|
let major = split.first().unwrap().parse::<u16>()?;
|
|
let minor = split.get(1).unwrap().parse::<u16>()?;
|
|
let patch = split.get(2).unwrap().parse::<u16>()?;
|
|
let build = split.get(3).unwrap().parse::<u16>()?;
|
|
if major > MAX_U12 || minor > MAX_U12 || patch > MAX_U12 || build > MAX_U12 {
|
|
return Err(Error::Range);
|
|
}
|
|
Ok(Self {
|
|
major,
|
|
minor,
|
|
patch,
|
|
build,
|
|
pre,
|
|
})
|
|
}
|
|
_ => Err(Error::ParseSemver),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Extended> for u64 {
|
|
fn from(value: Extended) -> Self {
|
|
let major = u64::from(value.major) << 52;
|
|
let minor = u64::from(value.minor) << 40;
|
|
let patch = u64::from(value.patch) << 28;
|
|
let build = u64::from(value.build) << 16;
|
|
let pre = u64::from(u16::from(value.pre));
|
|
major | minor | patch | build | pre
|
|
}
|
|
}
|
|
|
|
impl TryFrom<u64> for Extended {
|
|
type Error = Error;
|
|
|
|
fn try_from(value: u64) -> Result<Self, Self::Error> {
|
|
let mut mask: u64 = 0o7777 << 52;
|
|
let major = (value & mask) >> 52;
|
|
mask = 0o7777 << 40;
|
|
let minor = (value & mask) >> 40;
|
|
mask = 0o7777 << 28;
|
|
let patch = (value & mask) >> 28;
|
|
mask = 0o7777 << 16;
|
|
let build = (value & mask) >> 16;
|
|
mask = 0o37777;
|
|
let p = u16::try_from(value & mask)?;
|
|
let pre: PreRelease = p.try_into()?;
|
|
Ok(Self {
|
|
major: major.try_into()?,
|
|
minor: minor.try_into()?,
|
|
patch: patch.try_into()?,
|
|
build: build.try_into()?,
|
|
pre,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl PartialEq for Extended {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.major == other.major
|
|
&& self.minor == other.minor
|
|
&& self.patch == other.patch
|
|
&& self.build == other.build
|
|
&& self.pre == other.pre
|
|
}
|
|
}
|
|
|
|
impl Eq for Extended {}
|
|
|
|
impl Ord for Extended {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
let a = u64::from(*self);
|
|
let b = u64::from(*other);
|
|
a.cmp(&b)
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for Extended {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl From<Simple> for Extended {
|
|
fn from(value: Simple) -> Self {
|
|
Self {
|
|
major: value.major,
|
|
minor: 0,
|
|
patch: 0,
|
|
build: 0,
|
|
pre: value.pre,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Rapid> for Extended {
|
|
fn from(value: Rapid) -> Self {
|
|
Self {
|
|
major: value.major,
|
|
minor: value.minor,
|
|
patch: 0,
|
|
build: 0,
|
|
pre: value.pre,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<SemVer> for Extended {
|
|
fn from(value: SemVer) -> Self {
|
|
Self {
|
|
major: value.major,
|
|
minor: value.minor,
|
|
patch: value.patch,
|
|
build: 0,
|
|
pre: value.pre,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialEq<SemVer> for Extended {
|
|
fn eq(&self, other: &SemVer) -> bool {
|
|
self.major == other.major
|
|
&& self.minor == other.minor
|
|
&& self.patch == other.patch
|
|
&& self.build == 0
|
|
&& self.pre == other.pre
|
|
}
|
|
}
|
|
|
|
impl PartialEq<Rapid> for Extended {
|
|
fn eq(&self, other: &Rapid) -> bool {
|
|
self.major == other.major
|
|
&& self.minor == other.minor
|
|
&& self.patch == 0
|
|
&& self.build == 0
|
|
&& self.pre == other.pre
|
|
}
|
|
}
|
|
|
|
impl PartialEq<Simple> for Extended {
|
|
fn eq(&self, other: &Simple) -> bool {
|
|
self.major == other.major
|
|
&& self.minor == 0
|
|
&& self.patch == 0
|
|
&& self.build == 0
|
|
&& self.pre == other.pre
|
|
}
|
|
}
|
|
|
|
impl PartialEq<GitRev> for Extended {
|
|
fn eq(&self, _other: &GitRev) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl PartialOrd<SemVer> for Extended {
|
|
fn partial_cmp(&self, other: &SemVer) -> Option<Ordering> {
|
|
Some(u64::from(*self).cmp(&u64::from(*other)))
|
|
}
|
|
}
|
|
|
|
impl PartialOrd<Rapid> for Extended {
|
|
fn partial_cmp(&self, other: &Rapid) -> Option<Ordering> {
|
|
Some(u64::from(*self).cmp(&u64::from(*other)))
|
|
}
|
|
}
|
|
|
|
impl PartialOrd<Simple> for Extended {
|
|
fn partial_cmp(&self, other: &Simple) -> Option<Ordering> {
|
|
Some(u64::from(*self).cmp(&u64::from(*other)))
|
|
}
|
|
}
|
|
|
|
impl PartialOrd<GitRev> for Extended {
|
|
fn partial_cmp(&self, _other: &GitRev) -> Option<Ordering> {
|
|
None
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::num::NonZeroU16;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn from_str() {
|
|
let mut ex: Extended = "1.2.3.4".parse().unwrap();
|
|
assert_eq!(
|
|
ex,
|
|
Extended {
|
|
major: 1,
|
|
minor: 2,
|
|
patch: 3,
|
|
build: 4,
|
|
pre: PreRelease::None,
|
|
}
|
|
);
|
|
ex = "3.0.14.1_beta2".parse().unwrap();
|
|
assert_eq!(
|
|
ex,
|
|
Extended {
|
|
major: 3,
|
|
minor: 0,
|
|
patch: 14,
|
|
build: 1,
|
|
pre: PreRelease::Beta(Some(NonZeroU16::new(2).unwrap())),
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn to_string() {
|
|
let mut ex = Extended {
|
|
major: 2,
|
|
minor: 11,
|
|
patch: 0,
|
|
build: 1,
|
|
pre: PreRelease::None,
|
|
};
|
|
assert_eq!(ex.to_string(), "2.11.0.1");
|
|
ex.pre = PreRelease::RC(None);
|
|
assert_eq!(ex.to_string(), "2.11.0.1_rc");
|
|
ex.pre = PreRelease::Alpha(Some(NonZeroU16::new(3).unwrap()));
|
|
assert_eq!(ex.to_string(), "2.11.0.1_alpha3");
|
|
}
|
|
|
|
#[test]
|
|
fn eq() {
|
|
assert_eq!(
|
|
"1.0.2.1_beta1".parse::<Extended>().unwrap(),
|
|
"1.0.2.1_beta".parse::<Extended>().unwrap()
|
|
);
|
|
assert_ne!(
|
|
"1.0.2.1_alpha".parse::<Extended>().unwrap(),
|
|
"1.0.2.1_alpha2".parse::<Extended>().unwrap()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ord() {
|
|
let a: Extended = "1.0.14.1".parse().unwrap();
|
|
let b: Extended = "1.0.14.1_alpha4".parse().unwrap();
|
|
let c: Extended = "1.0.14.1_beta2".parse().unwrap();
|
|
let d: Extended = "1.0.14.1_beta3".parse().unwrap();
|
|
let e: Extended = "2.0.14.1".parse().unwrap();
|
|
let f: Extended = "2.1.14.1".parse().unwrap();
|
|
let g: Extended = "2.1.13.1".parse().unwrap();
|
|
assert!(a > b);
|
|
assert!(b < a);
|
|
assert!(a > c);
|
|
assert!(b < c);
|
|
assert!(c < a);
|
|
assert!(c > b);
|
|
assert!(d > b);
|
|
assert!(d < a);
|
|
assert!(e > a);
|
|
assert!(f > e);
|
|
assert!(g < f);
|
|
assert!(d < e);
|
|
assert!(e < f);
|
|
assert!(f > g);
|
|
}
|
|
}
|