use std::borrow::Cow;
use std::cmp::Ordering;
use std::fmt;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Nevra<'a> {
name: Cow<'a, str>,
evr: Evr<'a>,
arch: Cow<'a, str>,
}
impl<'a> Nevra<'a> {
pub fn new<T: Into<Cow<'a, str>>>(
name: T,
epoch: T,
version: T,
release: T,
arch: T,
) -> Nevra<'a> {
Self {
name: name.into(),
evr: Evr::new(epoch, version, release),
arch: arch.into(),
}
}
pub fn parse(nevra: &'a str) -> Self {
let (n, e, v, r, a) = Nevra::parse_values(nevra);
Self::new(n, e, v, r, a)
}
pub fn name(&self) -> &str {
&self.name
}
pub fn evr(&'a self) -> &'a Evr<'a> {
&self.evr
}
pub fn epoch(&self) -> &str {
&self.evr.epoch
}
pub fn version(&self) -> &str {
&self.evr.version
}
pub fn release(&self) -> &str {
&self.evr.release
}
pub fn arch(&self) -> &str {
&self.arch
}
pub fn values(&self) -> (&str, &str, &str, &str, &str) {
(
&self.name,
&self.evr.epoch,
&self.evr.version,
&self.evr.release,
&self.arch,
)
}
pub fn parse_values(nevra: &'a str) -> (&'a str, &'a str, &'a str, &'a str, &'a str) {
let (nevr, arch) = nevra.rsplit_once('.').unwrap_or((nevra, ""));
let (nev, release) = nevr.rsplit_once('-').unwrap_or((nevr, ""));
let (name, version_epoch) = nev.rsplit_once('-').unwrap_or((nev, ""));
let (epoch, version) = match version_epoch.split_once(':') {
Some((e, v)) => (e, v),
None => ("", version_epoch),
};
(name, epoch, version, release, arch)
}
pub fn as_normalized_form(&self) -> String {
format!(
"{}-{}.{}",
self.name,
self.evr.as_normalized_form(),
self.arch
)
}
pub fn nvra(&self) -> String {
format!(
"{}-{}-{}.{}",
self.name, self.evr.version, self.evr.release, self.arch
)
}
}
impl fmt::Display for Nevra<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}-{}.{}", self.name, self.evr, self.arch)
}
}
impl PartialOrd for Nevra<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Nevra<'_> {
fn cmp(&self, other: &Self) -> Ordering {
let name_cmp = compare_version_string(&self.name, &other.name);
if name_cmp != Ordering::Equal {
return name_cmp;
}
let evr_cmp = self.evr.cmp(&other.evr);
if evr_cmp != Ordering::Equal {
return evr_cmp;
}
compare_version_string(&self.arch, &other.arch)
}
}
#[derive(Clone, Debug, Default, Eq)]
pub struct Evr<'a> {
epoch: Cow<'a, str>,
version: Cow<'a, str>,
release: Cow<'a, str>,
}
impl<'a> Evr<'a> {
pub fn new<T: Into<Cow<'a, str>>>(epoch: T, version: T, release: T) -> Evr<'a> {
Evr {
epoch: epoch.into(),
version: version.into(),
release: release.into(),
}
}
pub fn parse(evr: &'a str) -> Self {
Evr::parse_values(evr).into()
}
pub fn epoch(&self) -> &str {
&self.epoch
}
pub fn version(&self) -> &str {
&self.version
}
pub fn release(&self) -> &str {
&self.release
}
pub fn as_normalized_form(&self) -> String {
let epoch = if self.epoch.is_empty() {
"0"
} else {
self.epoch.as_ref()
};
format!("{}:{}-{}", epoch, self.version(), self.release())
}
pub fn values(&self) -> (&str, &str, &str) {
(self.epoch(), self.version(), self.release())
}
pub fn parse_values(evr: &'a str) -> (&'a str, &'a str, &'a str) {
let (epoch, vr) = evr.split_once(':').unwrap_or(("", evr));
let (version, release) = vr.split_once('-').unwrap_or((vr, ""));
(epoch, version, release)
}
}
impl<'a> From<(&'a str, &'a str, &'a str)> for Evr<'a> {
fn from(val: (&'a str, &'a str, &'a str)) -> Self {
Evr::new(val.0, val.1, val.2)
}
}
impl PartialEq for Evr<'_> {
#[allow(clippy::comparison_to_empty)]
fn eq(&self, other: &Self) -> bool {
((self.epoch == other.epoch)
|| (self.epoch == "" && other.epoch == "0")
|| (self.epoch == "0" && other.epoch == ""))
&& self.version == other.version
&& self.release == other.release
}
}
impl fmt::Display for Evr<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.epoch.is_empty() {
write!(f, "{}:", self.epoch)?;
}
write!(f, "{}-{}", self.version, self.release)
}
}
impl PartialOrd for Evr<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Evr<'_> {
fn cmp(&self, other: &Self) -> Ordering {
let epoch_1 = if self.epoch.is_empty() {
"0"
} else {
&self.epoch
};
let epoch_2 = if other.epoch.is_empty() {
"0"
} else {
&other.epoch
};
let epoch_cmp = compare_version_string(epoch_1, epoch_2);
if epoch_cmp != Ordering::Equal {
return epoch_cmp;
}
let version_cmp = compare_version_string(&self.version, &other.version);
if version_cmp != Ordering::Equal {
return version_cmp;
}
compare_version_string(&self.release, &other.release)
}
}
fn compare_version_string(version1: &str, version2: &str) -> Ordering {
if version1 == version2 {
return Ordering::Equal;
}
let mut version1_part = version1;
let mut version2_part = version2;
let not_alphanumeric_tilde_or_caret =
|c: char| !c.is_ascii_alphanumeric() && c != '~' && c != '^';
loop {
version1_part = version1_part.trim_start_matches(not_alphanumeric_tilde_or_caret);
version2_part = version2_part.trim_start_matches(not_alphanumeric_tilde_or_caret);
match (
version1_part.strip_prefix('~'),
version2_part.strip_prefix('~'),
) {
(Some(_), None) => return Ordering::Less,
(None, Some(_)) => return Ordering::Greater,
(Some(a), Some(b)) => {
version1_part = a;
version2_part = b;
continue;
}
_ => (),
}
match (
version1_part.strip_prefix('^'),
version2_part.strip_prefix('^'),
) {
(Some(_), None) => match version2_part.is_empty() {
true => return Ordering::Greater,
false => return Ordering::Less,
},
(None, Some(_)) => match version1_part.is_empty() {
true => return Ordering::Less,
false => return Ordering::Greater,
},
(Some(a), Some(b)) => {
version1_part = a;
version2_part = b;
continue;
}
_ => (),
}
if version1_part.is_empty() || version2_part.is_empty() {
break;
}
fn matching_contiguous<F>(string: &str, pat: F) -> Option<(&str, &str)>
where
F: Fn(char) -> bool,
{
Some(
string.split_at(
string
.find(|c| !pat(c))
.or(Some(string.len()))
.filter(|&x| x > 0)?,
),
)
}
if version1_part.starts_with(|c: char| c.is_ascii_digit()) {
match (
matching_contiguous(version1_part, |c| c.is_ascii_digit()),
matching_contiguous(version2_part, |c| c.is_ascii_digit()),
) {
(Some((prefix1, rest1)), Some((prefix2, rest2))) => {
version1_part = rest1;
version2_part = rest2;
let prefix1 = prefix1.trim_start_matches('0');
let prefix2 = prefix2.trim_start_matches('0');
let ordering = prefix1.len().cmp(&prefix2.len());
if ordering != Ordering::Equal {
return ordering;
}
let ordering = prefix1.cmp(prefix2);
if ordering != Ordering::Equal {
return ordering;
}
}
(Some(_), None) => return Ordering::Greater,
_ => unreachable!(),
}
} else {
match (
matching_contiguous(version1_part, |c| c.is_ascii_alphabetic()),
matching_contiguous(version2_part, |c| c.is_ascii_alphabetic()),
) {
(Some((prefix1, rest1)), Some((prefix2, rest2))) => {
version1_part = rest1;
version2_part = rest2;
let ordering = prefix1.cmp(prefix2);
if ordering != Ordering::Equal {
return ordering;
}
}
(Some(_), None) => return Ordering::Less,
_ => unreachable!(),
}
}
}
version1_part.len().cmp(&version2_part.len())
}
pub fn rpm_evr_compare(evr1: &str, evr2: &str) -> Ordering {
let evr1 = Evr::parse(evr1);
let evr2 = Evr::parse(evr2);
evr1.cmp(&evr2)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_nevra_tostr() {
let nevra = Nevra::new("foo", "", "1.2.3", "45", "x86_64");
assert_eq!("foo-1.2.3-45.x86_64", nevra.to_string());
assert_eq!("foo-0:1.2.3-45.x86_64", nevra.as_normalized_form());
let nevra = Nevra::new("foo", "0", "1.2.3", "45", "x86_64");
assert_eq!("foo-0:1.2.3-45.x86_64", nevra.to_string());
assert_eq!("foo-0:1.2.3-45.x86_64", nevra.as_normalized_form());
let nevra = Nevra::new("foo", "1", "2.3.4", "5", "x86_64");
assert_eq!("foo-1:2.3.4-5.x86_64", nevra.to_string());
assert_eq!("foo-1:2.3.4-5.x86_64", nevra.as_normalized_form());
let nevra = Nevra::new("python3.9", "0", "3.9.11", "2.fc38", "x86_64");
assert_eq!("python3.9-0:3.9.11-2.fc38.x86_64", nevra.to_string());
assert_eq!(
"python3.9-0:3.9.11-2.fc38.x86_64",
nevra.as_normalized_form()
);
}
#[test]
fn test_nevra_parse() {
let nevra = Nevra::new("foo", "", "1.2.3", "45", "x86_64");
assert_eq!(Nevra::parse("foo-1.2.3-45.x86_64"), nevra);
let nevra = Nevra::new("foo", "0", "1.2.3", "45", "x86_64");
assert_eq!(Nevra::parse("foo-0:1.2.3-45.x86_64"), nevra);
let nevra = Nevra::new("foo", "1", "2.3.4", "5", "x86_64");
assert_eq!(Nevra::parse("foo-1:2.3.4-5.x86_64"), nevra);
let nevra = Nevra::new("python3.9", "0", "3.9.11", "2", "x86_64");
assert_eq!(Nevra::parse("python3.9-3.9.11-2.x86_64"), nevra);
let nevra = Nevra::new("python3.9", "0", "3.9.11", "2.fc38", "x86_64");
assert_eq!(Nevra::parse("python3.9-3.9.11-2.fc38.x86_64"), nevra);
}
#[test]
fn test_nevra_parse_edge_cases() {
assert_eq!(Nevra::parse_values("foo"), ("foo", "", "", "", ""));
assert_eq!(
Nevra::parse_values("foo-1.2-3.bar"),
("foo", "", "1.2", "3", "bar")
);
assert_eq!(
Nevra::parse_values("foo-1.2-3.bar.x86_64"),
("foo", "", "1.2", "3.bar", "x86_64")
);
assert_eq!(
Nevra::parse_values("python3.9-3.9.11-2.fc38.x86_64"),
("python3.9", "", "3.9.11", "2.fc38", "x86_64")
);
let nevra = Nevra::new("python3.9-devel", "0", "3.9.11", "2.fc38", "x86_64");
assert_eq!(Nevra::parse("python3.9-devel-3.9.11-2.fc38.x86_64"), nevra);
let nevra = Nevra::new("foo-bar", "", "1.2.3", "45", "x86_64");
assert_eq!(Nevra::parse("foo-bar-1.2.3-45.x86_64"), nevra);
let nevra = Nevra::new("foo-bar", "0", "1.2.3", "45", "x86_64");
assert_eq!(Nevra::parse("foo-bar-0:1.2.3-45.x86_64"), nevra);
let nevra = Nevra::new("foo-bar-0", "", "1.2.3", "45.el10", "x86_64");
assert_eq!(Nevra::parse("foo-bar-0-1.2.3-45.el10.x86_64"), nevra);
let nevra = Nevra::new("foo-bar-0", "0", "1.2.3", "45.el10", "x86_64");
assert_eq!(Nevra::parse("foo-bar-0-0:1.2.3-45.el10.x86_64"), nevra);
let nevra = Nevra::new("grub2-efi-x64", "1", "2.12", "28.fc42", "x86_64");
assert_eq!(Nevra::parse("grub2-efi-x64-1:2.12-28.fc42.x86_64"), nevra);
}
#[test]
fn test_nevra_ord() {
let nevra1 = Nevra::parse("foo-1.2.3-45.noarch");
let nevra2 = Nevra::parse("foo-1.2.3-45.noarch");
assert!(nevra1 == nevra2);
let nevra1 = Nevra::parse("foo-1.2.3-45.noarch");
let nevra2 = Nevra::parse("foo-0:1.2.3-45.noarch");
assert!(nevra1 == nevra2);
let nevra1 = Nevra::parse("bar-1.2.3-45.noarch");
let nevra2 = Nevra::parse("foo-9:1.2.3-45.noarch");
assert!(nevra1 < nevra2);
let nevra1 = Nevra::parse("foo-1.2.3-45.noarch");
let nevra2 = Nevra::parse("foobar-1.2.3-45.noarch");
assert!(nevra1 < nevra2);
let nevra1 = Nevra::parse("foo-2.3.4-5.noarch");
let nevra2 = Nevra::parse("foobar-1.2.3-45.noarch");
assert!(nevra1 < nevra2);
let nevra1 = Nevra::parse("bar-1.2.3-45.noarch");
let nevra2 = Nevra::parse("foo-1.2.3-45.noarch");
assert!(nevra1 < nevra2);
let nevra1 = Nevra::parse("foo-1.2.3-45.fc38.noarch");
let nevra2 = Nevra::parse("foo-1.2.3-45.fc39.noarch");
assert!(nevra1 < nevra2);
let nevra1 = Nevra::parse("foo-1.2.3-45.fc39.i386");
let nevra2 = Nevra::parse("foo-1.2.3-45.fc39.x86_64");
assert!(nevra1 < nevra2);
let nevra1 = Nevra::parse("python3.9-3.9.12-2.fc39.i386");
let nevra2 = Nevra::parse("python3.11-3.11.7-2.fc39.x86_64");
assert!(nevra1 < nevra2);
let nevra1 = Nevra::parse("python3.11-3.11.7-2.fc39.x86_64");
let nevra2 = Nevra::parse("python3.9-3.9.12-2.fc39.x86_64");
assert!(nevra1 > nevra2);
}
#[test]
fn test_evr_tostr() {
let evr = Evr::new("", "1.2.3", "45");
assert_eq!("1.2.3-45", evr.to_string());
assert_eq!("0:1.2.3-45", evr.as_normalized_form());
let evr = Evr::new("0", "1.2.3", "45");
assert_eq!("0:1.2.3-45", evr.to_string());
assert_eq!("0:1.2.3-45", evr.as_normalized_form());
}
#[test]
fn test_evr_parse() {
let evr = Evr::new("", "1.2.3", "45");
assert_eq!(Evr::parse("1.2.3-45"), evr);
let evr = Evr::new("0", "1.2.3", "45");
assert_eq!(Evr::parse("0:1.2.3-45"), evr);
let evr = Evr::new("1", "2.3.4", "5");
assert_eq!(Evr::parse("1:2.3.4-5"), evr);
}
#[test]
fn test_evr_parse_edge_cases() {
assert_eq!(Evr::parse_values("-"), ("", "", ""));
assert_eq!(Evr::parse_values("."), ("", ".", ""));
assert_eq!(Evr::parse_values(":"), ("", "", ""));
assert_eq!(Evr::parse_values(":-"), ("", "", ""));
assert_eq!(Evr::parse_values(".-"), ("", ".", ""));
assert_eq!(Evr::parse_values("0"), ("", "0", ""));
assert_eq!(Evr::parse_values("0-"), ("", "0", ""));
assert_eq!(Evr::parse_values(":0"), ("", "0", ""));
assert_eq!(Evr::parse_values(":0-"), ("", "0", ""));
assert_eq!(Evr::parse_values("0:"), ("0", "", ""));
assert_eq!(Evr::parse_values("asdf:"), ("asdf", "", ""));
assert_eq!(Evr::parse_values("~:"), ("~", "", ""));
}
#[test]
fn test_rpm_evr_compare() {
assert_eq!(Ordering::Equal, rpm_evr_compare("0:1.2.3-45", "1.2.3-45"));
assert_eq!(Ordering::Less, rpm_evr_compare("1.2.3-45", "1:1.2.3-45"));
assert_eq!(Ordering::Greater, rpm_evr_compare("1.2.3-46", "1.2.3-45"));
}
#[test]
fn test_evr_ord() {
let evr1 = Evr::parse("1.2.3-45");
let evr2 = Evr::parse("1.2.3-45");
assert!(evr1 == evr2);
let evr1 = Evr::parse("2:1.2.3-45");
let evr2 = Evr::parse("2:1.2.3-45");
assert!(evr1 == evr2);
let evr1 = Evr::parse("1.2.3-45");
let evr2 = Evr::parse("0:1.2.3-45");
assert!(evr1 == evr2);
let evr1 = Evr::parse("1.2.3-45");
let evr2 = Evr::parse("1:1.2.3-45");
assert!(evr1 < evr2);
let evr1 = Evr::parse("4.2.3-45");
let evr2 = Evr::parse("1:1.2.3-45");
assert!(evr1 < evr2);
let evr1 = Evr::parse("1.2.3-45");
let evr2 = Evr::parse("1.2.4-45");
assert!(evr1 < evr2);
let evr1 = Evr::parse("1.23.3-45");
let evr2 = Evr::parse("1.2.3-45");
assert!(evr1 > evr2);
let evr1 = Evr::parse("12.2.3-45");
let evr2 = Evr::parse("1.2.3-45");
assert!(evr1 > evr2);
let evr1 = Evr::parse("1.2.3-45");
let evr2 = Evr::parse("1.12.3-45");
assert!(evr1 < evr2);
let evr1 = Evr::parse("~1.2.3-45");
let evr2 = Evr::parse("1.2.3-45");
assert!(evr1 < evr2);
let evr1 = Evr::parse("~12.2.3-45");
let evr2 = Evr::parse("1.2.3-45");
assert!(evr1 < evr2);
let evr1 = Evr::parse("~12.2.3-45");
let evr2 = Evr::parse("~1.2.3-45");
assert!(evr1 > evr2);
let evr1 = Evr::parse("~3:12.2.3-45");
let evr2 = Evr::parse("0:1.2.3-45");
assert!(evr1 < evr2);
let evr1 = Evr::parse("1.2.3-45");
let evr2 = Evr::parse("1.2.3-46");
assert!(evr1 < evr2);
let evr1 = Evr::parse("1.2.3-45.fc39");
let evr2 = Evr::parse("1.2.3-46.fc38");
assert!(evr1 < evr2);
let evr1 = Evr::parse("1.2.3-3");
let evr2 = Evr::parse("1.2.3-10");
assert!(evr1 < evr2);
let evr1 = Evr::parse("1.2.3-3.fc40");
let evr2 = Evr::parse("1.2.3-10.fc39");
assert!(evr1 < evr2);
}
#[test]
fn test_compare_version_string() {
assert_eq!(Ordering::Equal, compare_version_string("1.0", "1.0"));
assert_eq!(Ordering::Less, compare_version_string("1.0", "2.0"));
assert_eq!(Ordering::Greater, compare_version_string("2.0", "1.0"));
assert_eq!(Ordering::Equal, compare_version_string("2.0.1", "2.0.1"));
assert_eq!(Ordering::Less, compare_version_string("2.0", "2.0.1"));
assert_eq!(Ordering::Greater, compare_version_string("2.0.1", "2.0"));
assert_eq!(Ordering::Less, compare_version_string("5.0.1", "5.0.1a"));
assert_eq!(Ordering::Greater, compare_version_string("5.0.1a", "5.0.1"));
assert_eq!(Ordering::Equal, compare_version_string("5.0.a1", "5.0.a1"));
assert_eq!(Ordering::Equal, compare_version_string("5.0.1a", "5.0.1a"));
assert_eq!(Ordering::Less, compare_version_string("5.0.a1", "5.0.a2"));
assert_eq!(
Ordering::Greater,
compare_version_string("5.0.a2", "5.0.a1")
);
assert_eq!(Ordering::Less, compare_version_string("10abc", "10.1abc"));
assert_eq!(
Ordering::Greater,
compare_version_string("10.1abc", "10abc")
);
assert_eq!(Ordering::Less, compare_version_string("8.0", "8.0.rc1"));
assert_eq!(Ordering::Greater, compare_version_string("8.0.rc1", "8.0"));
assert_eq!(Ordering::Greater, compare_version_string("10b2", "10a1"));
assert_eq!(Ordering::Less, compare_version_string("10a2", "10b2"));
assert_eq!(Ordering::Less, compare_version_string("6.6p1", "7.5p1"));
assert_eq!(Ordering::Greater, compare_version_string("7.5p1", "6.6p1"));
assert_eq!(Ordering::Equal, compare_version_string("6.5p1", "6.5p1"));
assert_eq!(Ordering::Less, compare_version_string("6.5p1", "6.5p2"));
assert_eq!(Ordering::Greater, compare_version_string("6.5p2", "6.5p1"));
assert_eq!(Ordering::Less, compare_version_string("6.5p2", "6.6p1"));
assert_eq!(Ordering::Greater, compare_version_string("6.6p1", "6.5p2"));
assert_eq!(Ordering::Equal, compare_version_string("6.5p10", "6.5p10"));
assert_eq!(Ordering::Less, compare_version_string("6.5p1", "6.5p10"));
assert_eq!(Ordering::Greater, compare_version_string("6.5p10", "6.5p1"));
assert_eq!(Ordering::Equal, compare_version_string("abc10", "abc10"));
assert_eq!(Ordering::Less, compare_version_string("abc10", "abc10.1"));
assert_eq!(
Ordering::Greater,
compare_version_string("abc10.1", "abc10")
);
assert_eq!(Ordering::Equal, compare_version_string("abc.4", "abc.4"));
assert_eq!(Ordering::Less, compare_version_string("abc.4", "8"));
assert_eq!(Ordering::Greater, compare_version_string("8", "abc.4"));
assert_eq!(Ordering::Less, compare_version_string("abc.4", "2"));
assert_eq!(Ordering::Greater, compare_version_string("2", "abc.4"));
assert_eq!(Ordering::Equal, compare_version_string("1.0aa", "1.0aa"));
assert_eq!(Ordering::Less, compare_version_string("1.0a", "1.0aa"));
assert_eq!(Ordering::Greater, compare_version_string("1.0aa", "1.0a"));
}
#[test]
fn test_version_comparison_numeric_handling() {
assert_eq!(
Ordering::Equal,
compare_version_string("10.0001", "10.0001")
);
assert_eq!(Ordering::Equal, compare_version_string("10.0001", "10.1"));
assert_eq!(Ordering::Equal, compare_version_string("10.1", "10.0001"));
assert_eq!(Ordering::Less, compare_version_string("10.0001", "10.0039"));
assert_eq!(
Ordering::Greater,
compare_version_string("10.0039", "10.0001")
);
assert_eq!(Ordering::Less, compare_version_string("10.1", "10.10001"));
assert_eq!(
Ordering::Less,
compare_version_string("10.1111", "10.10001")
);
assert_eq!(
Ordering::Greater,
compare_version_string("10.11111", "10.10001")
);
assert_eq!(
Ordering::Equal,
compare_version_string("20240521", "20240521")
);
assert_eq!(
Ordering::Less,
compare_version_string("20240521", "20240522")
);
assert_eq!(
Ordering::Greater,
compare_version_string("20240522", "20240521")
);
assert_eq!(
Ordering::Less,
compare_version_string("20240521", "202405210")
);
}
#[test]
fn test_version_comparison_tilde_and_caret() {
assert_eq!(
Ordering::Equal,
compare_version_string("1.0~rc1", "1.0~rc1")
);
assert_eq!(Ordering::Less, compare_version_string("1.0~rc1", "1.0"));
assert_eq!(Ordering::Greater, compare_version_string("1.0", "1.0~rc1"));
assert_eq!(Ordering::Less, compare_version_string("1.0~rc1", "1.0~rc2"));
assert_eq!(
Ordering::Greater,
compare_version_string("1.0~rc2", "1.0~rc1")
);
assert_eq!(
Ordering::Equal,
compare_version_string("1.0~rc1~git123", "1.0~rc1~git123")
);
assert_eq!(
Ordering::Less,
compare_version_string("1.0~rc1~git123", "1.0~rc1")
);
assert_eq!(
Ordering::Greater,
compare_version_string("1.0~rc1", "1.0~rc1~git123")
);
assert_eq!(Ordering::Equal, compare_version_string("1.0^", "1.0^"));
assert_eq!(Ordering::Less, compare_version_string("1.0", "1.0^"));
assert_eq!(Ordering::Greater, compare_version_string("1.0^", "1.0"));
assert_eq!(Ordering::Less, compare_version_string("1.0", "1.0git1^"));
assert_eq!(
Ordering::Less,
compare_version_string("1.0^git1", "1.0^git2")
);
assert_eq!(
Ordering::Greater,
compare_version_string("1.01", "1.0^git1")
);
assert_eq!(
Ordering::Equal,
compare_version_string("1.0^20240501", "1.0^20240501")
);
assert_eq!(
Ordering::Less,
compare_version_string("1.0^20240501", "1.0.1")
);
assert_eq!(
Ordering::Equal,
compare_version_string("1.0^20240501^git1", "1.0^20240501^git1")
);
assert_eq!(
Ordering::Greater,
compare_version_string("1.0^20240502", "1.0^20240501^git1")
);
assert_eq!(
Ordering::Equal,
compare_version_string("1.0~rc1^git1", "1.0~rc1^git1")
);
assert_eq!(
Ordering::Less,
compare_version_string("1.0~rc1", "1.0~rc1^git1")
);
assert_eq!(
Ordering::Greater,
compare_version_string("1.0~rc1^git1", "1.0~rc1")
);
assert_eq!(
Ordering::Equal,
compare_version_string("1.0^git1~pre", "1.0^git1~pre")
);
assert_eq!(
Ordering::Less,
compare_version_string("1.0^git1~pre", "1.0^git1")
);
assert_eq!(
Ordering::Greater,
compare_version_string("1.0^git1", "1.0^git1~pre")
);
}
#[test]
fn test_non_intuitive_comparison_behavior() {
assert_eq!(Ordering::Less, compare_version_string("1e.fc33", "1.fc33"));
assert_eq!(
Ordering::Greater,
compare_version_string("1g.fc33", "1.fc33")
);
}
#[test]
fn test_non_alphanumeric_equivalence() {
assert_eq!(Ordering::Equal, compare_version_string("b", "b"));
assert_eq!(Ordering::Equal, compare_version_string("b+", "b+"));
assert_eq!(Ordering::Equal, compare_version_string("b+", "b_"));
assert_eq!(Ordering::Equal, compare_version_string("b_", "b+"));
assert_eq!(Ordering::Equal, compare_version_string("+b", "+b"));
assert_eq!(Ordering::Equal, compare_version_string("+b", "_b"));
assert_eq!(Ordering::Equal, compare_version_string("_b", "+b"));
assert_eq!(Ordering::Equal, compare_version_string("+b", "++b"));
assert_eq!(Ordering::Equal, compare_version_string("+b", "+b+"));
assert_eq!(Ordering::Equal, compare_version_string("+.", "+_"));
assert_eq!(Ordering::Equal, compare_version_string("_+", "+."));
assert_eq!(Ordering::Equal, compare_version_string("+", "."));
assert_eq!(Ordering::Equal, compare_version_string(",", "+"));
assert_eq!(Ordering::Equal, compare_version_string("++", "_"));
assert_eq!(Ordering::Equal, compare_version_string("+", ".."));
assert_eq!(Ordering::Equal, compare_version_string("4_0", "4_0"));
assert_eq!(Ordering::Equal, compare_version_string("4_0", "4.0"));
assert_eq!(Ordering::Equal, compare_version_string("4.0", "4_0"));
assert_eq!(Ordering::Less, compare_version_string("4.999", "5.0"));
assert_eq!(Ordering::Less, compare_version_string("4.999.9", "5.0"));
assert_eq!(Ordering::Greater, compare_version_string("5.0", "4.999_9"));
assert_eq!(Ordering::Less, compare_version_string("4.999", "4.999.9"));
assert_eq!(Ordering::Greater, compare_version_string("4.999", "4.99.9"));
}
#[test]
fn test_non_ascii_character_equivalence() {
assert_eq!(Ordering::Equal, compare_version_string("1.1.Á.1", "1.1.1"));
assert_eq!(Ordering::Equal, compare_version_string("1.1.Á", "1.1.Á"));
assert_eq!(Ordering::Equal, compare_version_string("1.1.Á", "1.1.Ê"));
assert_eq!(Ordering::Equal, compare_version_string("1.1.ÁÁ", "1.1.Á"));
assert_eq!(Ordering::Equal, compare_version_string("1.1.Á", "1.1.ÊÊ"));
assert_eq!(Ordering::Less, compare_version_string("1.1Á1", "1.11"));
}
}