From 800957576505f3a8c12ef5106d40819d24148009 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Fri, 5 Jan 2024 19:05:34 -0500 Subject: [PATCH] Add `SemVer` struct and related impl's --- src/error.rs | 13 +++++++- src/lib.rs | 5 ++- src/semver.rs | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 2 deletions(-) diff --git a/src/error.rs b/src/error.rs index 793738f..fd08412 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,8 +1,13 @@ -use std::{fmt, num::TryFromIntError}; +use std::{ + fmt, + num::{ParseIntError, TryFromIntError}, +}; #[derive(Debug, PartialEq)] pub enum Error { FromUint, + Range, + ParseInt, ParsePreRelease, ParseSemver, TryFromInt, @@ -21,3 +26,9 @@ impl From for Error { Self::TryFromInt } } + +impl From for Error { + fn from(_value: ParseIntError) -> Self { + Self::ParseInt + } +} diff --git a/src/lib.rs b/src/lib.rs index 6ff5fc2..7069b1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,7 @@ mod error; mod prerelease; +mod semver; -pub use {error::Error, prerelease::PreRelease}; +pub use {error::Error, prerelease::PreRelease, semver::SemVer}; + +pub static MAX_U12: u16 = 4096; diff --git a/src/semver.rs b/src/semver.rs index e69de29..c39809f 100644 --- a/src/semver.rs +++ b/src/semver.rs @@ -0,0 +1,87 @@ +use { + crate::{Error, PreRelease, MAX_U12}, + serde::{Deserialize, Serialize}, + std::{cmp::Ordering, fmt, str::FromStr}, +}; + +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +pub struct SemVer { + pub major: u16, + pub minor: u16, + pub patch: u16, + pub pre: PreRelease, +} + +impl fmt::Display for SemVer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?; + match self.pre { + PreRelease::None => Ok(()), + p => write!(f, "_{p}"), + } + } +} + +impl FromStr for SemVer { + 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::()?; + if major > MAX_U12 || minor > MAX_U12 || patch > MAX_U12 { + return Err(Error::Range); + } + Ok(Self { + major, + minor, + patch, + pre, + }) + } + _ => Err(Error::ParseSemver), + } + } +} + +impl From for u64 { + fn from(value: SemVer) -> Self { + let major = u64::from(value.major) << 52; + let minor = u64::from(value.minor) << 40; + let patch = u64::from(value.patch) << 28; + let pre = u64::from(u16::from(value.pre)); + major | minor | patch | pre + } +} + +impl PartialEq for SemVer { + fn eq(&self, other: &Self) -> bool { + self.major == other.major + && self.minor == other.minor + && self.patch == other.patch + && self.pre == other.pre + } +} + +impl Eq for SemVer {} + +impl Ord for SemVer { + fn cmp(&self, other: &Self) -> Ordering { + let a = u64::from(*self); + let b = u64::from(*other); + a.cmp(&b) + } +} + +impl PartialOrd for SemVer { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +}