diff --git a/src/extended.rs b/src/extended.rs index e69de29..670bfee 100644 --- a/src/extended.rs +++ b/src/extended.rs @@ -0,0 +1,121 @@ +use { + crate::{Error, PreRelease, 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 { + let (s, pre) = match s.split_once('_') { + Some((a, b)) => (a, b.parse::()?), + None => (s, PreRelease::None), + }; + let split = s.split('.').collect::>(); + match split.len() { + 3 => { + let major = split.first().unwrap().parse::()?; + let minor = split.get(1).unwrap().parse::()?; + let patch = split.get(2).unwrap().parse::()?; + let build = split.get(3).unwrap().parse::()?; + 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 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) << 14; + let pre = u64::from(u16::from(value.pre)); + major | minor | patch | build | pre + } +} + +impl TryFrom for Extended { + type Error = Error; + + fn try_from(value: u64) -> Result { + 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 << 14; + let build = (value & mask) >> 14; + 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 { + Some(self.cmp(other)) + } +} diff --git a/src/lib.rs b/src/lib.rs index 7069b1e..3558809 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,8 @@ mod error; +mod extended; mod prerelease; mod semver; -pub use {error::Error, prerelease::PreRelease, semver::SemVer}; +pub use {error::Error, extended::Extended, prerelease::PreRelease, semver::SemVer}; pub static MAX_U12: u16 = 4096; diff --git a/src/semver.rs b/src/semver.rs index 576b1eb..9dfd282 100644 --- a/src/semver.rs +++ b/src/semver.rs @@ -144,7 +144,18 @@ mod tests { #[test] fn eq() { - todo!() + assert_eq!( + "1.0.0_alpha".parse::().unwrap(), + "1.0.0_alpha1".parse::().unwrap(), + ); + assert_eq!( + "2.1.3_beta".parse::().unwrap(), + "2.1.3_beta1".parse::().unwrap(), + ); + assert_eq!( + "1.11.0_rc".parse::().unwrap(), + "1.11.0_rc1".parse::().unwrap(), + ); } #[test]