1
Fork 0
mirror of https://github.com/RGBCube/dix synced 2025-09-12 18:57:59 +00:00

feat: refactor version comparisions

This commit is contained in:
RGBCube 2025-05-08 22:40:25 +03:00 committed by bloxx12
parent 574142c4a0
commit af7f63d3f6

View file

@ -18,6 +18,7 @@ type Result<T> = std::result::Result<T, AppError>;
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<Ordering> {
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<I: Into<&'a str>>(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<Self::Item> {
// 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::<u64>().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),