use crate::SemVer; use { crate::Version, serde::{Deserialize, Serialize}, std::{cmp::Ordering, error::Error, fmt, num::ParseIntError, str::FromStr}, }; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct Rapid { pub major: u32, pub minor: u32, } impl fmt::Display for Rapid { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}.{}", self.major, self.minor) } } impl PartialOrd for Rapid { fn partial_cmp(&self, other: &Self) -> Option { match self.major.partial_cmp(&other.major) { Some(Ordering::Greater) => Some(Ordering::Greater), Some(Ordering::Less) => Some(Ordering::Less), Some(Ordering::Equal) => self.minor.partial_cmp(&other.minor), None => None, } } } impl Ord for Rapid { fn cmp(&self, other: &Self) -> std::cmp::Ordering { match self.major.cmp(&other.major) { Ordering::Greater => Ordering::Greater, Ordering::Less => Ordering::Less, Ordering::Equal => self.minor.cmp(&other.minor), } } } impl PartialEq for Rapid { fn eq(&self, other: &u32) -> bool { self.major == *other && self.minor == 0 } } impl PartialOrd for Rapid { fn partial_cmp(&self, other: &u32) -> Option { match self.major.partial_cmp(other) { Some(Ordering::Greater) => Some(Ordering::Greater), Some(Ordering::Less) => Some(Ordering::Less), None => None, Some(Ordering::Equal) => { if self.minor == 0 { Some(Ordering::Equal) } else { Some(Ordering::Greater) } } } } } impl PartialEq for Rapid { fn eq(&self, other: &SemVer) -> bool { other.eq(self) } } impl PartialOrd for Rapid { fn partial_cmp(&self, other: &SemVer) -> Option { match other.partial_cmp(self) { Some(Ordering::Less) => Some(Ordering::Greater), Some(Ordering::Greater) => Some(Ordering::Less), Some(Ordering::Equal) => Some(Ordering::Equal), None => None, } } } impl PartialEq for u32 { fn eq(&self, other: &Rapid) -> bool { other.eq(self) } } impl PartialOrd for u32 { fn partial_cmp(&self, other: &Rapid) -> Option { match other.partial_cmp(self) { Some(Ordering::Equal) => Some(Ordering::Equal), Some(Ordering::Less) => Some(Ordering::Greater), Some(Ordering::Greater) => Some(Ordering::Less), None => None, } } } #[derive(Debug, PartialEq)] pub struct ParseRapidError; impl fmt::Display for ParseRapidError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Error parsing Rapid version") } } impl Error for ParseRapidError {} impl From for ParseRapidError { fn from(_value: ParseIntError) -> Self { Self } } impl FromStr for Rapid { type Err = ParseRapidError; fn from_str(s: &str) -> Result { let split = s.split('.').collect::>(); match split.len() { 2 => { let major = split.first().unwrap().parse::()?; let minor = split.get(1).unwrap().parse::()?; Ok(Self { major, minor }) } _ => Err(ParseRapidError), } } } impl TryFrom for Rapid { type Error = ParseRapidError; fn try_from(value: Version) -> Result { match value { Version::SemVer(s) => { if s.patch == 0 { Ok(Self { major: s.major, minor: s.minor, }) } else { Err(ParseRapidError) } } Version::Rapid(s) => Ok(s), Version::Number(major) => Ok(Self { major, minor: 0 }), Version::Git(_) => Err(ParseRapidError), } } } #[cfg(test)] mod test { use super::*; #[test] fn parse_semver() { assert_eq!( "93.0".parse::(), Ok(Rapid { major: 93, minor: 0, }) ); } #[test] fn rapid_num_eq() { let rapid = Rapid { major: 42, minor: 0 }; assert_eq!(rapid, 42); } #[test] fn rapid_num_gt() { let rapid = Rapid { major: 1, minor: 42 }; assert!(rapid > 1); } #[test] fn rapid_semver_eq() { let rapid = Rapid { major: 42, minor: 69 }; let semver = SemVer { major: 42, minor: 69, patch: 0 }; assert_eq!(rapid, semver); } #[test] fn rapid_semver_lt() { let rapid = Rapid { major: 42, minor: 69 }; let semver = SemVer { major: 42, minor: 69, patch: 1 }; assert!(rapid < semver); } }