From af7f63d3f6e9bde6d06a61f785a33d6e824b25eb Mon Sep 17 00:00:00 2001 From: RGBCube Date: Thu, 8 May 2025 22:40:25 +0300 Subject: [PATCH] feat: refactor version comparisions --- src/util.rs | 92 ++++++++++++++++++++--------------------------------- 1 file changed, 35 insertions(+), 57 deletions(-) diff --git a/src/util.rs b/src/util.rs index b042447..e3ec990 100644 --- a/src/util.rs +++ b/src/util.rs @@ -18,6 +18,7 @@ type Result = std::result::Result; use std::string::ToString; #[derive(Eq, PartialEq, Debug)] +#[derive(Debug, Clone, Eq, PartialEq)] enum VersionComponent { Number(u64), Text(String), @@ -29,72 +30,53 @@ impl std::cmp::Ord for VersionComponent { Number, Text, }; + match (self, other) { (Number(x), Number(y)) => x.cmp(y), (Text(x), Text(y)) => { match (x.as_str(), y.as_str()) { - ("pre", _) => Ordering::Less, - (_, "pre") => Ordering::Greater, + ("pre", _) => cmp::Ordering::Less, + (_, "pre") => cmp::Ordering::Greater, _ => x.cmp(y), } }, - (Text(_), Number(_)) => Ordering::Less, - (Number(_), Text(_)) => Ordering::Greater, + (Text(_), Number(_)) => cmp::Ordering::Less, + (Number(_), Text(_)) => cmp::Ordering::Greater, } } } -impl PartialOrd for VersionComponent { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} +/// Yields [`VertionComponent`] from a version string. +#[derive(Deref, DerefMut, From)] +struct VersionComponentIter<'a>(&'a str); -// takes a version string and outputs the different components -// -// a component is delimited by '-' or '.' and consists of just digits or letters -struct VersionComponentIterator<'a> { - v: &'a [u8], - pos: usize, -} - -impl<'a> VersionComponentIterator<'a> { - pub fn new>(v: I) -> Self { - Self { - v: v.into().as_bytes(), - pos: 0, - } - } -} - -impl Iterator for VersionComponentIterator<'_> { +impl Iterator for VersionComponentIter<'_> { type Item = VersionComponent; fn next(&mut self) -> Option { - // skip all '-' and '.' in the beginning - while let Some(b'.' | b'-') = self.v.get(self.pos) { - self.pos += 1; + // Skip all '-' and '.'. + while self.starts_with(['.', '-']) { + **self = &self[1..]; } - // get the next character and decide if it is a digit or char - let c = self.v.get(self.pos)?; - let is_digit = c.is_ascii_digit(); - // based on this collect characters after this into the component - let component_len = self.v[self.pos..] - .iter() - .copied() - .take_while(|&c| c.is_ascii_digit() == is_digit && c != b'.' && c != b'-') - .count(); - let component = - String::from_utf8_lossy(&self.v[self.pos..(self.pos + component_len)]) - .into_owned(); + // Get the next character and decide if it is a digit. + let is_digit = self.chars().next()?.is_ascii_digit(); - // remember what chars we used - self.pos += component_len; + // Based on this collect characters after this into the component. + let component_len = self + .chars() + .take_while(|&c| { + c.is_ascii_digit() == is_digit && !matches!(c, '.' | '-') + }) + .map(char::len_utf8) + .sum(); - if component.is_empty() { - None - } else if is_digit { + let component = self[..component_len].to_owned(); + **self = &self[component_len..]; + + assert!(!component.is_empty()); + + if is_digit { component.parse::().ok().map(VersionComponent::Number) } else { Some(VersionComponent::Text(component)) @@ -103,15 +85,11 @@ impl Iterator for VersionComponentIterator<'_> { } /// Compares two strings of package versions, and figures out the greater one. -/// -/// # Returns -/// -/// * Ordering -pub fn compare_versions(a: &str, b: &str) -> Ordering { - let iter_a = VersionComponentIterator::new(a); - let iter_b = VersionComponentIterator::new(b); +pub fn compare_versions(this: &str, that: &str) -> cmp::Ordering { + let this = VersionComponentIter::from(this); + let that = VersionComponentIter::from(that); - iter_a.cmp(iter_b) + this.cmp(that) } /// Parses a nix store path to extract the packages name and version @@ -244,10 +222,10 @@ mod test { Number, Text, }; - use crate::util::VersionComponentIterator; + use crate::util::VersionComponentIter; let v = "132.1.2test234-1-man----.--.......---------..---"; - let comp: Vec<_> = VersionComponentIterator::new(v).collect(); + let comp: Vec<_> = VersionComponentIter::new(v).collect(); assert_eq!(comp, [ Number(132), Number(1),