mirror of
https://github.com/RGBCube/dix
synced 2025-09-13 11:17:58 +00:00
feat: refactor version comparisions
This commit is contained in:
parent
574142c4a0
commit
af7f63d3f6
1 changed files with 35 additions and 57 deletions
92
src/util.rs
92
src/util.rs
|
@ -18,6 +18,7 @@ type Result<T> = std::result::Result<T, AppError>;
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug)]
|
#[derive(Eq, PartialEq, Debug)]
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
enum VersionComponent {
|
enum VersionComponent {
|
||||||
Number(u64),
|
Number(u64),
|
||||||
Text(String),
|
Text(String),
|
||||||
|
@ -29,72 +30,53 @@ impl std::cmp::Ord for VersionComponent {
|
||||||
Number,
|
Number,
|
||||||
Text,
|
Text,
|
||||||
};
|
};
|
||||||
|
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Number(x), Number(y)) => x.cmp(y),
|
(Number(x), Number(y)) => x.cmp(y),
|
||||||
(Text(x), Text(y)) => {
|
(Text(x), Text(y)) => {
|
||||||
match (x.as_str(), y.as_str()) {
|
match (x.as_str(), y.as_str()) {
|
||||||
("pre", _) => Ordering::Less,
|
("pre", _) => cmp::Ordering::Less,
|
||||||
(_, "pre") => Ordering::Greater,
|
(_, "pre") => cmp::Ordering::Greater,
|
||||||
_ => x.cmp(y),
|
_ => x.cmp(y),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(Text(_), Number(_)) => Ordering::Less,
|
(Text(_), Number(_)) => cmp::Ordering::Less,
|
||||||
(Number(_), Text(_)) => Ordering::Greater,
|
(Number(_), Text(_)) => cmp::Ordering::Greater,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for VersionComponent {
|
/// Yields [`VertionComponent`] from a version string.
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
#[derive(Deref, DerefMut, From)]
|
||||||
Some(self.cmp(other))
|
struct VersionComponentIter<'a>(&'a str);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// takes a version string and outputs the different components
|
impl Iterator for VersionComponentIter<'_> {
|
||||||
//
|
|
||||||
// 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<'_> {
|
|
||||||
type Item = VersionComponent;
|
type Item = VersionComponent;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
// skip all '-' and '.' in the beginning
|
// Skip all '-' and '.'.
|
||||||
while let Some(b'.' | b'-') = self.v.get(self.pos) {
|
while self.starts_with(['.', '-']) {
|
||||||
self.pos += 1;
|
**self = &self[1..];
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the next character and decide if it is a digit or char
|
// Get the next character and decide if it is a digit.
|
||||||
let c = self.v.get(self.pos)?;
|
let is_digit = self.chars().next()?.is_ascii_digit();
|
||||||
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();
|
|
||||||
|
|
||||||
// remember what chars we used
|
// Based on this collect characters after this into the component.
|
||||||
self.pos += component_len;
|
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() {
|
let component = self[..component_len].to_owned();
|
||||||
None
|
**self = &self[component_len..];
|
||||||
} else if is_digit {
|
|
||||||
|
assert!(!component.is_empty());
|
||||||
|
|
||||||
|
if is_digit {
|
||||||
component.parse::<u64>().ok().map(VersionComponent::Number)
|
component.parse::<u64>().ok().map(VersionComponent::Number)
|
||||||
} else {
|
} else {
|
||||||
Some(VersionComponent::Text(component))
|
Some(VersionComponent::Text(component))
|
||||||
|
@ -103,15 +85,11 @@ impl Iterator for VersionComponentIterator<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compares two strings of package versions, and figures out the greater one.
|
/// Compares two strings of package versions, and figures out the greater one.
|
||||||
///
|
pub fn compare_versions(this: &str, that: &str) -> cmp::Ordering {
|
||||||
/// # Returns
|
let this = VersionComponentIter::from(this);
|
||||||
///
|
let that = VersionComponentIter::from(that);
|
||||||
/// * Ordering
|
|
||||||
pub fn compare_versions(a: &str, b: &str) -> Ordering {
|
|
||||||
let iter_a = VersionComponentIterator::new(a);
|
|
||||||
let iter_b = VersionComponentIterator::new(b);
|
|
||||||
|
|
||||||
iter_a.cmp(iter_b)
|
this.cmp(that)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses a nix store path to extract the packages name and version
|
/// Parses a nix store path to extract the packages name and version
|
||||||
|
@ -244,10 +222,10 @@ mod test {
|
||||||
Number,
|
Number,
|
||||||
Text,
|
Text,
|
||||||
};
|
};
|
||||||
use crate::util::VersionComponentIterator;
|
use crate::util::VersionComponentIter;
|
||||||
let v = "132.1.2test234-1-man----.--.......---------..---";
|
let v = "132.1.2test234-1-man----.--.......---------..---";
|
||||||
|
|
||||||
let comp: Vec<_> = VersionComponentIterator::new(v).collect();
|
let comp: Vec<_> = VersionComponentIter::new(v).collect();
|
||||||
assert_eq!(comp, [
|
assert_eq!(comp, [
|
||||||
Number(132),
|
Number(132),
|
||||||
Number(1),
|
Number(1),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue