diff --git a/Cargo.lock b/Cargo.lock index 6068d2d..c1adbfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,9 +46,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" dependencies = [ "android-tzdata", "iana-time-zone", @@ -56,7 +56,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -65,6 +65,14 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "epoch" +version = "0.1.0" +source = "git+https://git.hitchhiker-linux.org/jeang3nie/epoch-rs.git#9563b1da0d9e985555d2deff2ca0ee279bd04bee" +dependencies = [ + "serde", +] + [[package]] name = "iana-time-zone" version = "0.1.59" @@ -144,18 +152,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.195" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", @@ -184,6 +192,7 @@ name = "version" version = "0.1.0" dependencies = [ "chrono", + "epoch", "serde", ] @@ -247,22 +256,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -271,93 +265,51 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index e5545d9..986b573 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" [dependencies] chrono = { version = "0.4", features = ["serde"] } +epoch = { git = "https://git.hitchhiker-linux.org/jeang3nie/epoch-rs.git", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } diff --git a/src/extended.rs b/src/extended.rs deleted file mode 100644 index dbd5d01..0000000 --- a/src/extended.rs +++ /dev/null @@ -1,306 +0,0 @@ -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 { - 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() { - 4 => { - 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) << 16; - 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 << 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 { - Some(self.cmp(other)) - } -} - -impl From for Extended { - fn from(value: Simple) -> Self { - Self { - major: value.major, - minor: 0, - patch: 0, - build: 0, - pre: value.pre, - } - } -} - -impl From for Extended { - fn from(value: Rapid) -> Self { - Self { - major: value.major, - minor: value.minor, - patch: 0, - build: 0, - pre: value.pre, - } - } -} - -impl From for Extended { - fn from(value: SemVer) -> Self { - Self { - major: value.major, - minor: value.minor, - patch: value.patch, - build: 0, - pre: value.pre, - } - } -} - -impl PartialEq 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 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 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 for Extended { - fn eq(&self, _other: &GitRev) -> bool { - false - } -} - -impl PartialOrd for Extended { - fn partial_cmp(&self, other: &SemVer) -> Option { - Some(u64::from(*self).cmp(&u64::from(*other))) - } -} - -impl PartialOrd for Extended { - fn partial_cmp(&self, other: &Rapid) -> Option { - Some(u64::from(*self).cmp(&u64::from(*other))) - } -} - -impl PartialOrd for Extended { - fn partial_cmp(&self, other: &Simple) -> Option { - Some(u64::from(*self).cmp(&u64::from(*other))) - } -} - -impl PartialOrd for Extended { - fn partial_cmp(&self, _other: &GitRev) -> Option { - 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::().unwrap(), - "1.0.2.1_beta".parse::().unwrap() - ); - assert_ne!( - "1.0.2.1_alpha".parse::().unwrap(), - "1.0.2.1_alpha2".parse::().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); - } -} diff --git a/src/gitrev.rs b/src/gitrev.rs deleted file mode 100644 index 5d0da71..0000000 --- a/src/gitrev.rs +++ /dev/null @@ -1,175 +0,0 @@ -use { - crate::{ - error::Error, extended::Extended, rapid::Rapid, semver::SemVer, simple::Simple, - version::Kind, version::Version, - }, - chrono::{offset::Utc, DateTime}, - serde::{Deserialize, Serialize}, - std::{cmp::Ordering, fmt, str::FromStr}, -}; - -/// Represents a Git revisionrather than a regular release. -/// A `GitRev` release contains the revision's hash and the Date/Time of the release. -/// Git revisions are not the preferred method of distribution for obvious reasons, as -/// they generally do not represent a stable release of the code. Another drawback is -/// that a Git revision can only be compared against another Git revision, making it -/// impossible to properly order updates between a proper `SemVer` type release and a\ -/// Git revision. -/// ### Notes on display formatting -/// The hash as stored in this struct should be the short form revision hash (the first -/// 7 characters of the full hash). The full date and time information is saved minus -/// any fractional seconds. When displaying, the date and time should be converted to a -/// Unix timestamp. -/// ### Parsing from a string -/// In order to parse this struct from a string, two fields `hash` and `datetime` should -/// be separated by a period character '.' and the date/time take the form of a Unix -/// timestamp. This information can be retrieved from git for a given commit using the -/// following git commandline: -/// ```Sh -/// git show --pretty=format:"%h.%at" | head -n 1 -/// ``` -/// The resulting string can then be parsed using the `FromStr` trait, which provides -/// both `from_str` and `parse`. -/// ### Comparison - ordering -/// Git revisions are ordered by date/time -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct GitRev { - /// the short revision hash - pub hash: String, - /// the time of the revision commit - pub datetime: DateTime, -} - -impl fmt::Display for GitRev { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "git_{}.{}", self.hash, self.datetime.timestamp()) - } -} - -impl PartialOrd for GitRev { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for GitRev { - fn cmp(&self, other: &Self) -> Ordering { - self.datetime.cmp(&other.datetime) - } -} - -impl FromStr for GitRev { - type Err = Error; - - fn from_str(s: &str) -> Result { - if let Some(gitrev) = s.strip_prefix("git_") { - if let Some((hash, datetime)) = gitrev.split_once('.') { - if hash.len() == 7 { - let secs: i64 = datetime.parse()?; - let Some(datetime) = DateTime::::from_timestamp(secs, 0) else { - return Err(Error::ParseInt); - }; - return Ok(Self { - hash: hash.to_string(), - datetime, - }); - } - } - } - Err(Error::ParseGitRev) - } -} - -impl PartialEq for GitRev { - fn eq(&self, _other: &Extended) -> bool { - false - } -} - -impl PartialOrd for GitRev { - fn partial_cmp(&self, _other: &Extended) -> Option { - None - } -} - -impl PartialEq for GitRev { - fn eq(&self, _other: &SemVer) -> bool { - false - } -} - -impl PartialOrd for GitRev { - fn partial_cmp(&self, _other: &SemVer) -> Option { - None - } -} - -impl PartialEq for GitRev { - fn eq(&self, _other: &Rapid) -> bool { - false - } -} - -impl PartialOrd for GitRev { - fn partial_cmp(&self, _other: &Rapid) -> Option { - None - } -} - -impl PartialEq for GitRev { - fn eq(&self, _other: &Simple) -> bool { - false - } -} - -impl PartialOrd for GitRev { - fn partial_cmp(&self, _other: &Simple) -> Option { - None - } -} - -impl TryFrom for GitRev { - type Error = Error; - - fn try_from(value: Version) -> Result { - match value.0 { - Kind::GitRev(g) => Ok(g), - _ => Err(Error::ParseGitRev), - } - } -} - -#[cfg(test)] -mod test { - use std::{thread, time::Duration}; - - use super::*; - - #[test] - fn ord() { - let a = GitRev { - hash: "aaabxxx".to_string(), - datetime: Utc::now(), - }; - thread::sleep(Duration::from_millis(10)); - let b = GitRev { - hash: "aaaaxxx".to_string(), - datetime: Utc::now(), - }; - assert!(a < b); - } - - #[test] - fn from_str() { - let now = DateTime::::from_timestamp(Utc::now().timestamp(), 0).unwrap(); - let rev: GitRev = format!("git_r2d2xxx.{}", now.timestamp()).parse().unwrap(); - println!("Version = {rev}"); - assert_eq!( - rev, - GitRev { - hash: "r2d2xxx".to_string(), - datetime: now, - } - ); - } -} diff --git a/src/lib.rs b/src/lib.rs index 1d50456..122e139 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,13 +3,8 @@ mod arch; mod error; -mod extended; -mod gitrev; pub mod prelude; mod prerelease; -mod rapid; -mod semver; -mod simple; mod version; pub use version::Version; diff --git a/src/prelude.rs b/src/prelude.rs index b599f4d..83c93dc 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,4 +1 @@ -pub use crate::{ - arch::Arch, error::Error as VersionError, extended::Extended, gitrev::GitRev, rapid::Rapid, - semver::SemVer, simple::Simple, version::Kind as VersionKind, -}; +pub use crate::{arch::Arch, error::Error as VersionError, version::Kind as VersionKind}; diff --git a/src/prerelease.rs b/src/prerelease.rs index 40e4e2c..68d55b0 100644 --- a/src/prerelease.rs +++ b/src/prerelease.rs @@ -1,5 +1,6 @@ use { crate::{error::Error, MAX_U12}, + epoch::DateTime, serde::{Deserialize, Serialize}, std::{cmp, convert::Into, fmt, num::NonZeroU16, str}, }; @@ -10,31 +11,45 @@ pub enum PreRelease { Alpha(Option), Beta(Option), RC(Option), + Git { + hash: [char; 7], + datetime: DateTime, + }, /// `PreRelease::None` is equivalent to a normal release None, } impl PreRelease { - pub fn is_prerelease(self) -> bool { + pub fn is_prerelease(&self) -> bool { !matches!(self, Self::None) } - pub fn is_alpha(self) -> bool { + pub fn is_alpha(&self) -> bool { matches!(self, Self::Alpha(_)) } - pub fn is_beta(self) -> bool { + pub fn is_beta(&self) -> bool { matches!(self, Self::Beta(_)) } - pub fn is_rc(self) -> bool { + pub fn is_rc(&self) -> bool { matches!(self, Self::RC(_)) } + pub fn is_gitrev(&self) -> bool { + matches!( + self, + Self::Git { + hash: _, + datetime: _ + } + ) + } + pub fn number(self) -> Option { match self { Self::Alpha(n) | Self::Beta(n) | Self::RC(n) => n.map(Into::into), - Self::None => None, + _ => None, } } @@ -52,7 +67,7 @@ impl PreRelease { } None => *n = Some(NonZeroU16::new(2).unwrap()), }, - Self::None => {} + _ => {} } Ok(()) } @@ -65,7 +80,7 @@ impl PreRelease { Self::Alpha(_) => *self = Self::Beta(None), Self::Beta(_) => *self = Self::RC(None), Self::RC(_) => *self = Self::None, - Self::None => {} + _ => {} } } } @@ -73,12 +88,19 @@ impl PreRelease { impl fmt::Display for PreRelease { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Alpha(Some(v)) => write!(f, "alpha{v}"), - Self::Alpha(None) => write!(f, "alpha"), - Self::Beta(Some(v)) => write!(f, "beta{v}"), - Self::Beta(None) => write!(f, "beta"), - Self::RC(Some(v)) => write!(f, "rc{v}"), - Self::RC(None) => write!(f, "rc"), + Self::Alpha(Some(v)) => write!(f, "_alpha{v}"), + Self::Alpha(None) => write!(f, "_alpha"), + Self::Beta(Some(v)) => write!(f, "_beta{v}"), + Self::Beta(None) => write!(f, "_beta"), + Self::RC(Some(v)) => write!(f, "_rc{v}"), + Self::RC(None) => write!(f, "_rc"), + Self::Git { hash, datetime } => { + write!(f, "_git_")?; + for c in hash { + write!(f, "{c}")?; + } + write!(f, ".{}", datetime.timestamp()) + } Self::None => Ok(()), } } @@ -142,57 +164,50 @@ impl str::FromStr for PreRelease { _ => Err(Error::ParsePreRelease), } } + s if s.starts_with("git_") => { + let s = s.strip_prefix("git_").unwrap(); + let Some((h, ts)) = s.split_once('.') else { + return Err(Error::ParseGitRev); + }; + if h.len() != 7 { + return Err(Error::ParseGitRev); + } + let mut hash = ['x'; 7]; + for (idx, c) in h.chars().enumerate() { + hash[idx] = c; + } + let ts: i64 = ts.parse()?; + let datetime = DateTime::from_timestamp(ts); + Ok(Self::Git { hash, datetime }) + } _ => Err(Error::ParsePreRelease), } } } -impl From for u16 { +impl From for u128 { fn from(value: PreRelease) -> Self { - let mask: u16 = 0o1_777; + let mask: u16 = 0o3_777; match value { + PreRelease::Git { hash: _, datetime } => { + 0o4_000 << 64 | u128::try_from(datetime.timestamp()).unwrap() + } PreRelease::Alpha(Some(v)) => { let v = u16::from(v) & mask; - v | 0o10_000 + u128::from(v | 0o10_000) << 64 } - PreRelease::Alpha(None) => 0o2_000, + PreRelease::Alpha(None) => 0o10_000 << 64, PreRelease::Beta(Some(v)) => { let v = u16::from(v) & mask; - v | 0o20_000 + u128::from(v | 0o20_000) << 64 } - PreRelease::Beta(None) => 0o4_000, + PreRelease::Beta(None) => 0o20_000 << 64, PreRelease::RC(Some(v)) => { let v = u16::from(v) & mask; - v | 0o40_000 + u128::from(v | 0o40_000) << 64 } - PreRelease::RC(None) => 0o10_000, - PreRelease::None => 0o100_000, - } - } -} - -impl TryFrom for PreRelease { - type Error = crate::error::Error; - - fn try_from(value: u16) -> Result { - let mask = 0o1_777; - let v = value & mask; - let flag = value & !mask; - match flag { - 0o10_000 => { - let v = if v > 0 { Some(v.try_into()?) } else { None }; - Ok(Self::Alpha(v)) - } - 0o20_000 => { - let v = if v > 0 { Some(v.try_into()?) } else { None }; - Ok(Self::Beta(v)) - } - 0o40_000 => { - let v = if v > 0 { Some(v.try_into()?) } else { None }; - Ok(Self::RC(v)) - } - 0o100_000 => Ok(Self::None), - _ => Err(Error::FromUint), + PreRelease::RC(None) => 0o40_000 << 64, + PreRelease::None => 0o100_000 << 64, } } } @@ -211,8 +226,8 @@ impl PartialEq for PreRelease { n == 1 } _ => { - let a: u16 = (*self).into(); - let b: u16 = (*other).into(); + let a: u128 = (*self).into(); + let b: u128 = (*other).into(); a == b } } @@ -223,8 +238,8 @@ impl Eq for PreRelease {} impl cmp::Ord for PreRelease { fn cmp(&self, other: &Self) -> cmp::Ordering { - let a: u16 = (*self).into(); - let b: u16 = (*other).into(); + let a: u128 = (*self).into(); + let b: u128 = (*other).into(); a.cmp(&b) } } diff --git a/src/rapid.rs b/src/rapid.rs deleted file mode 100644 index acbcc53..0000000 --- a/src/rapid.rs +++ /dev/null @@ -1,159 +0,0 @@ -use { - crate::{ - error::Error, extended::Extended, gitrev::GitRev, prerelease::PreRelease, semver::SemVer, - simple::Simple, MAX_U12, - }, - serde::{Deserialize, Serialize}, - std::{cmp::Ordering, fmt, str::FromStr}, -}; - -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] -pub struct Rapid { - pub major: u16, - pub minor: u16, - pub pre: PreRelease, -} - -impl fmt::Display for Rapid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}.{}", self.major, self.minor)?; - match self.pre { - PreRelease::None => Ok(()), - p => write!(f, "_{p}"), - } - } -} - -impl FromStr for Rapid { - 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() { - 2 => { - let major = split.first().unwrap().parse::()?; - let minor = split.get(1).unwrap().parse::()?; - if major > MAX_U12 || minor > MAX_U12 { - return Err(Error::Range); - } - Ok(Self { major, minor, pre }) - } - _ => Err(Error::ParseSemver), - } - } -} - -impl From for u64 { - fn from(value: Rapid) -> Self { - let major = u64::from(value.major) << 52; - let minor = u64::from(value.minor) << 40; - let pre = u64::from(u16::from(value.pre)); - major | minor | pre - } -} - -impl TryFrom for Rapid { - 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 = 0o37777; - let p = u16::try_from(value & mask)?; - let pre: PreRelease = p.try_into()?; - Ok(Self { - major: major.try_into()?, - minor: minor.try_into()?, - pre, - }) - } -} - -impl PartialEq for Rapid { - fn eq(&self, other: &Self) -> bool { - self.major == other.major && self.minor == other.minor && self.pre == other.pre - } -} - -impl Eq for Rapid {} - -impl Ord for Rapid { - fn cmp(&self, other: &Self) -> Ordering { - let a = u64::from(*self); - let b = u64::from(*other); - a.cmp(&b) - } -} - -impl PartialOrd for Rapid { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl PartialEq for Rapid { - fn eq(&self, other: &Extended) -> bool { - self.major == other.major - && self.minor == other.minor - && other.patch == 0 - && other.build == 0 - && self.pre == other.pre - } -} - -impl PartialEq for Rapid { - fn eq(&self, other: &SemVer) -> bool { - self.major == other.major - && self.minor == other.minor - && other.patch == 0 - && self.pre == other.pre - } -} - -impl PartialEq for Rapid { - fn eq(&self, other: &Simple) -> bool { - self.major == other.major && self.minor == 0 && self.pre == other.pre - } -} - -impl PartialEq for Rapid { - fn eq(&self, _other: &GitRev) -> bool { - false - } -} - -impl PartialOrd for Rapid { - fn partial_cmp(&self, other: &Extended) -> Option { - let a = u64::from(*self); - let b = u64::from(*other); - a.partial_cmp(&b) - } -} - -impl PartialOrd for Rapid { - fn partial_cmp(&self, other: &SemVer) -> Option { - let a = u64::from(*self); - let b = u64::from(*other); - a.partial_cmp(&b) - } -} - -impl PartialOrd for Rapid { - fn partial_cmp(&self, other: &Simple) -> Option { - let a = u64::from(*self); - let b = u64::from(*other); - a.partial_cmp(&b) - } -} - -impl PartialOrd for Rapid { - fn partial_cmp(&self, _other: &GitRev) -> Option { - None - } -} diff --git a/src/semver.rs b/src/semver.rs deleted file mode 100644 index ce53845..0000000 --- a/src/semver.rs +++ /dev/null @@ -1,260 +0,0 @@ -use { - crate::{ - error::Error, extended::Extended, gitrev::GitRev, prerelease::PreRelease, rapid::Rapid, - simple::Simple, 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 TryFrom for SemVer { - 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 = 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()?, - 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)) - } -} - -impl PartialEq for SemVer { - fn eq(&self, other: &Extended) -> bool { - self.major == other.major - && self.minor == other.minor - && self.patch == other.patch - && other.build == 0 - && self.pre == other.pre - } -} - -impl PartialEq for SemVer { - fn eq(&self, other: &Rapid) -> bool { - self.major == other.major - && self.minor == other.minor - && self.patch == 0 - && self.pre == other.pre - } -} - -impl PartialEq for SemVer { - fn eq(&self, other: &Simple) -> bool { - self.major == other.major && self.minor == 0 && self.patch == 0 && self.pre == other.pre - } -} - -impl PartialEq for SemVer { - fn eq(&self, _other: &GitRev) -> bool { - false - } -} - -impl PartialOrd for SemVer { - fn partial_cmp(&self, other: &Extended) -> Option { - let a = u64::from(*self); - let b = u64::from(*other); - Some(a.cmp(&b)) - } -} - -impl PartialOrd for SemVer { - fn partial_cmp(&self, other: &Rapid) -> Option { - let a = u64::from(*self); - let b = u64::from(*other); - Some(a.cmp(&b)) - } -} - -impl PartialOrd for SemVer { - fn partial_cmp(&self, other: &Simple) -> Option { - let a = u64::from(*self); - let b = u64::from(*other); - Some(a.cmp(&b)) - } -} - -impl PartialOrd for SemVer { - fn partial_cmp(&self, _other: &GitRev) -> Option { - None - } -} - -#[cfg(test)] -mod tests { - use std::num::NonZeroU16; - - use super::*; - - #[test] - fn from_str() { - let mut s: SemVer = "1.2.3".parse().unwrap(); - assert_eq!( - s, - SemVer { - major: 1, - minor: 2, - patch: 3, - pre: PreRelease::None, - } - ); - s = "0.3.0_alpha4".parse().unwrap(); - assert_eq!( - s, - SemVer { - major: 0, - minor: 3, - patch: 0, - pre: PreRelease::Alpha(Some(NonZeroU16::new(4).unwrap())), - } - ) - } - - #[test] - fn to_string() { - let mut s = SemVer { - major: 1, - minor: 0, - patch: 2, - pre: PreRelease::None, - }; - assert_eq!(s.to_string(), "1.0.2"); - s = SemVer { - major: 2, - minor: 1, - patch: 14, - pre: PreRelease::Beta(Some(NonZeroU16::new(2).unwrap())), - }; - assert_eq!(s.to_string(), "2.1.14_beta2"); - } - - #[test] - fn to_from_u64() { - let sem = SemVer { - major: 1, - minor: 0, - patch: 11, - pre: PreRelease::Beta(Some(NonZeroU16::new(2).unwrap())), - }; - assert_eq!(SemVer::try_from(u64::from(sem)).unwrap(), sem); - } - - #[test] - fn eq() { - 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] - fn ord() { - let a: SemVer = "1.0.2".parse().unwrap(); - let b: SemVer = "1.0.3".parse().unwrap(); - let c: SemVer = "1.0.2_alpha1".parse().unwrap(); - let d: SemVer = "1.0.2_alpha2".parse().unwrap(); - assert!(a < b); - assert!(c < a); - assert!(d > c); - } -} diff --git a/src/simple.rs b/src/simple.rs deleted file mode 100644 index 82fbdbe..0000000 --- a/src/simple.rs +++ /dev/null @@ -1,144 +0,0 @@ -use { - crate::{ - error::Error, extended::Extended, gitrev::GitRev, prerelease::PreRelease, rapid::Rapid, - semver::SemVer, MAX_U12, - }, - serde::{Deserialize, Serialize}, - std::{cmp::Ordering, fmt, str::FromStr}, -}; - -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] -pub struct Simple { - pub major: u16, - pub pre: PreRelease, -} - -impl fmt::Display for Simple { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.major)?; - match self.pre { - PreRelease::None => Ok(()), - p => write!(f, "_{p}"), - } - } -} - -impl FromStr for Simple { - 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 major = s.parse::()?; - if major > MAX_U12 { - return Err(Error::Range); - } - Ok(Self { major, pre }) - } -} - -impl From for u64 { - fn from(value: Simple) -> Self { - let major = u64::from(value.major) << 52; - let pre = u64::from(u16::from(value.pre)); - major | pre - } -} - -impl TryFrom for Simple { - type Error = Error; - - fn try_from(value: u64) -> Result { - let mut mask: u64 = 0o7777 << 52; - let major = (value & mask) >> 52; - mask = 0o37777; - let p = u16::try_from(value & mask)?; - let pre: PreRelease = p.try_into()?; - Ok(Self { - major: major.try_into()?, - pre, - }) - } -} - -impl PartialEq for Simple { - fn eq(&self, other: &Self) -> bool { - self.major == other.major && self.pre == other.pre - } -} - -impl Eq for Simple {} - -impl Ord for Simple { - fn cmp(&self, other: &Self) -> Ordering { - let a = u64::from(*self); - let b = u64::from(*other); - a.cmp(&b) - } -} - -impl PartialOrd for Simple { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl PartialEq for Simple { - fn eq(&self, other: &Extended) -> bool { - self.major == other.major - && other.minor == 0 - && other.patch == 0 - && other.build == 0 - && self.pre == other.pre - } -} - -impl PartialEq for Simple { - fn eq(&self, other: &SemVer) -> bool { - self.major == other.major && other.minor == 0 && other.patch == 0 && self.pre == other.pre - } -} - -impl PartialEq for Simple { - fn eq(&self, other: &Rapid) -> bool { - self.major == other.major && other.minor == 0 && self.pre == other.pre - } -} - -impl PartialEq for Simple { - fn eq(&self, _other: &GitRev) -> bool { - false - } -} - -impl PartialOrd for Simple { - fn partial_cmp(&self, other: &Extended) -> Option { - let a = u64::from(*self); - let b = u64::from(*other); - Some(a.cmp(&b)) - } -} - -impl PartialOrd for Simple { - fn partial_cmp(&self, other: &SemVer) -> Option { - let a = u64::from(*self); - let b = u64::from(*other); - Some(a.cmp(&b)) - } -} - -impl PartialOrd for Simple { - fn partial_cmp(&self, other: &Rapid) -> Option { - let a = u64::from(*self); - let b = u64::from(*other); - Some(a.cmp(&b)) - } -} - -impl PartialOrd for Simple { - fn partial_cmp(&self, _other: &GitRev) -> Option { - None - } -} diff --git a/src/version.rs b/src/version.rs index 2aed14e..93bda84 100644 --- a/src/version.rs +++ b/src/version.rs @@ -1,202 +1,215 @@ use { - crate::{ - arch::Arch, error::Error, extended::Extended, gitrev::GitRev, rapid::Rapid, semver::SemVer, - simple::Simple, - }, + crate::{arch::Arch, error::Error, prerelease::PreRelease, MAX_U12}, serde::{Deserialize, Serialize}, - std::{fmt, str::FromStr}, + std::{cmp, fmt, str}, }; -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] pub enum Kind { - Simple(Simple), - Rapid(Rapid), - SemVer(SemVer), - Extended(Extended), - GitRev(GitRev), + 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, Debug, Deserialize, Serialize)] -pub struct Version(pub Kind, pub Arch); +#[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 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 { - match &self.0 { - Kind::Simple(s) => write!(f, "{s}-{}", self.1), - Kind::Rapid(r) => write!(f, "{r}-{}", self.1), - Kind::SemVer(s) => write!(f, "{s}-{}", self.1), - Kind::Extended(x) => write!(f, "{x}-{}", self.1), - Kind::GitRev(g) => write!(f, "{g}-{}", self.1), - } + write!(f, "{}{}-{}", self.kind, self.pre, self.arch) } } -impl FromStr for Version { +impl From 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 { - let Some((s, Some(arch))) = s.split_once('-').map(|(s, a)| (s, a.parse::().ok())) - else { + let Some((s, arch)) = s.split_once('-') else { return Err(Error::ParseArch); }; - if let Ok(v) = Simple::from_str(s) { - Ok(Self(Kind::Simple(v), arch)) - } else if let Ok(v) = Rapid::from_str(s) { - Ok(Self(Kind::Rapid(v), arch)) - } else if let Ok(v) = SemVer::from_str(s) { - Ok(Self(Kind::SemVer(v), arch)) - } else if let Ok(v) = Extended::from_str(s) { - Ok(Self(Kind::Extended(v), arch)) - } else if let Ok(v) = GitRev::from_str(s) { - Ok(Self(Kind::GitRev(v), arch)) - } else { - Err(Error::ParseVersion) - } - } -} - -impl From for Kind { - fn from(value: Simple) -> Self { - Self::Simple(value) - } -} - -impl From for Kind { - fn from(value: Rapid) -> Self { - Self::Rapid(value) - } -} - -impl From for Kind { - fn from(value: SemVer) -> Self { - Self::SemVer(value) - } -} - -impl From for Kind { - fn from(value: Extended) -> Self { - Self::Extended(value) - } -} - -impl From for Kind { - fn from(value: GitRev) -> Self { - Self::GitRev(value) - } -} - -impl PartialEq for Kind { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::Simple(a), Self::Simple(b)) => a == b, - (Self::Simple(a), Self::Rapid(b)) => a == b, - (Self::Simple(a), Self::SemVer(b)) => a == b, - (Self::Simple(a), Self::Extended(b)) => a == b, - (Self::Rapid(a), Self::Simple(b)) => a == b, - (Self::Rapid(a), Self::Rapid(b)) => a == b, - (Self::Rapid(a), Self::SemVer(b)) => a == b, - (Self::Rapid(a), Self::Extended(b)) => a == b, - (Self::SemVer(a), Self::Simple(b)) => a == b, - (Self::SemVer(a), Self::Rapid(b)) => a == b, - (Self::SemVer(a), Self::SemVer(b)) => a == b, - (Self::SemVer(a), Self::Extended(b)) => a == b, - (Self::Extended(a), Self::Simple(b)) => a == b, - (Self::Extended(a), Self::Rapid(b)) => a == b, - (Self::Extended(a), Self::SemVer(b)) => a == b, - (Self::Extended(a), Self::Extended(b)) => a == b, - (Self::GitRev(a), Self::GitRev(b)) => a == b, - _ => false, - } - } -} - -impl Eq for Kind {} - -impl PartialOrd for Kind { - fn partial_cmp(&self, other: &Self) -> Option { - match (self, other) { - (Self::Simple(a), Self::Simple(b)) => a.partial_cmp(b), - (Self::Simple(a), Self::Rapid(b)) => a.partial_cmp(b), - (Self::Simple(a), Self::SemVer(b)) => a.partial_cmp(b), - (Self::Simple(a), Self::Extended(b)) => a.partial_cmp(b), - (Self::Rapid(a), Self::Simple(b)) => a.partial_cmp(b), - (Self::Rapid(a), Self::Rapid(b)) => a.partial_cmp(b), - (Self::Rapid(a), Self::SemVer(b)) => a.partial_cmp(b), - (Self::Rapid(a), Self::Extended(b)) => a.partial_cmp(b), - (Self::SemVer(a), Self::Simple(b)) => a.partial_cmp(b), - (Self::SemVer(a), Self::Rapid(b)) => a.partial_cmp(b), - (Self::SemVer(a), Self::SemVer(b)) => a.partial_cmp(b), - (Self::SemVer(a), Self::Extended(b)) => a.partial_cmp(b), - (Self::Extended(a), Self::Simple(b)) => a.partial_cmp(b), - (Self::Extended(a), Self::Rapid(b)) => a.partial_cmp(b), - (Self::Extended(a), Self::SemVer(b)) => a.partial_cmp(b), - (Self::Extended(a), Self::Extended(b)) => a.partial_cmp(b), - (Self::GitRev(a), Self::GitRev(b)) => a.partial_cmp(b), - _ => None, + let arch: Arch = arch.parse()?; + let (s, pre) = match s.split_once('_') { + Some((a, b)) => (a, b.parse::()?), + None => (s, PreRelease::None), + }; + let mut split = s.split('.'); + let Some(Ok(major)) = split.next().map(|m| m.parse::()) 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 { - self.0 == other.0 && self.1 == other.1 + 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 { - if self.1 == other.1 { - self.0.partial_cmp(&other.0) + fn partial_cmp(&self, other: &Self) -> Option { + if self.arch == other.arch { + Some(self.cmp(other)) } else { None } } } - -#[cfg(test)] -mod tests { - use chrono::{DateTime, Utc}; - use std::num::NonZeroU16; - - use super::*; - - #[test] - fn from_str() { - let mut version: Version = "2.4.1_alpha2-aarch64".parse().unwrap(); - assert_eq!( - version, - Version( - Kind::SemVer(SemVer { - major: 2, - minor: 4, - patch: 1, - pre: crate::prerelease::PreRelease::Alpha(Some(NonZeroU16::new(2).unwrap())) - }), - Arch::Arm64 - ) - ); - version = "6.4-i486".parse().unwrap(); - assert_eq!( - version, - Version( - Kind::Rapid(Rapid { - major: 6, - minor: 4, - pre: crate::prerelease::PreRelease::None, - }), - Arch::X86, - ) - ); - version = "git_r2d2xxx.1705881493-amd64".parse().unwrap(); - assert_eq!( - version, - Version( - Kind::GitRev(GitRev { - hash: "r2d2xxx".to_string(), - datetime: DateTime::::from_timestamp(1705881493, 0).unwrap(), - }), - Arch::X86_64, - ) - ); - } -}