From da723c704fd814f4efcb21f710c43a356364ed28 Mon Sep 17 00:00:00 2001 From: Dragyx <66752602+Dragyx@users.noreply.github.com> Date: Wed, 7 May 2025 12:49:22 +0200 Subject: [PATCH] util: Replace regex with custom iterator --- src/util.rs | 99 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 19 deletions(-) diff --git a/src/util.rs b/src/util.rs index ed44251..a313cd3 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,8 +1,6 @@ -use regex::Regex; use std::cmp::Ordering; -use std::sync::OnceLock; -#[derive(Eq, PartialEq)] +#[derive(Eq, PartialEq, Debug)] enum VersionComponent { Number(u64), Text(String), @@ -30,30 +28,93 @@ impl PartialOrd for VersionComponent { } } +// 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 str, + pos: usize, +} + +impl<'a> VersionComponentIterator<'a> { + pub fn new>(v: I) -> Self { + Self { + v: v.into(), + pos: 0, + } + } +} + +impl Iterator for VersionComponentIterator<'_> { + type Item = VersionComponent; + + fn next(&mut self) -> Option { + // check if there is something left + if self.pos >= self.v.len() { + return None; + } + + // skip all '-' and '.' in the beginning + let mut skipped_chars = 0; + let mut chars = self.v[self.pos..].chars().peekable(); + while let Some('.' | '-') = chars.peek() { + skipped_chars += 1; + chars.next(); + } + + // get the next character and decide if it is a digit or char + let c = chars.peek()?; + let is_digit = c.is_ascii_digit(); + // based on this collect characters after this into the component + let component: String = chars + .take_while(|&c| c.is_ascii_digit() == is_digit && c != '.' && c != '-') + .collect(); + + // remember what chars we used + self.pos += component.len() + skipped_chars; + + if component.is_empty() { + None + } else if is_digit { + component.parse::().ok().map(VersionComponent::Number) + } else { + Some(VersionComponent::Text(component)) + } + } +} + /// 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 = version_split_regex().find_iter(a).map(|m| { - use VersionComponent::{Number, Text}; - let bla = m.as_str(); - bla.parse().map_or_else(|_| Text(bla.to_string()), Number) - }); - - let iter_b = version_split_regex().find_iter(b).map(|m| { - use VersionComponent::{Number, Text}; - let bla = m.as_str(); - bla.parse().map_or_else(|_| Text(bla.to_string()), Number) - }); + let iter_a = VersionComponentIterator::new(a); + let iter_b = VersionComponentIterator::new(b); iter_a.cmp(iter_b) } -fn version_split_regex() -> &'static Regex { - static REGEX: OnceLock = OnceLock::new(); - REGEX.get_or_init(|| { - Regex::new(r"(\d+|[a-zA-Z]+)").expect("Failed to compile regex pattern for nix store paths") - }) +mod test { + + #[test] + fn test_version_component_iter() { + use super::VersionComponent::{Number, Text}; + use crate::util::VersionComponentIterator; + let v = "132.1.2test234-1-man----.--.......---------..---"; + + let comp: Vec<_> = VersionComponentIterator::new(v).collect(); + assert_eq!( + comp, + [ + Number(132), + Number(1), + Number(2), + Text("test".into()), + Number(234), + Number(1), + Text("man".into()) + ] + ); + } }