forked from gentoo-utils/gentoo-utils
780 lines
20 KiB
Rust
780 lines
20 KiB
Rust
use core::{
|
|
fmt::{self},
|
|
option::Option,
|
|
};
|
|
use std::cmp::Ordering;
|
|
|
|
use crate::useflag::UseFlag;
|
|
|
|
use get::Get;
|
|
|
|
use itertools::Itertools;
|
|
|
|
mod parsers;
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
pub enum Blocker {
|
|
Weak,
|
|
Strong,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
pub enum VersionOperator {
|
|
Lt,
|
|
Gt,
|
|
Eq,
|
|
LtEq,
|
|
GtEq,
|
|
Roughly,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
|
|
pub struct Category(#[get(method = "get", kind = "deref")] String);
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
|
|
pub struct Name(#[get(method = "get", kind = "deref")] String);
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
|
|
pub struct VersionNumber(#[get(method = "get", kind = "deref")] String);
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Get)]
|
|
struct VersionNumbers(#[get(method = "get", kind = "deref")] Vec<VersionNumber>);
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub enum VersionSuffixKind {
|
|
Alpha,
|
|
Beta,
|
|
Pre,
|
|
Rc,
|
|
P,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Get)]
|
|
pub struct VersionSuffix {
|
|
kind: VersionSuffixKind,
|
|
number: Option<VersionNumber>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Get)]
|
|
pub struct VersionSuffixes(#[get(method = "get", kind = "deref")] Vec<VersionSuffix>);
|
|
|
|
#[derive(Debug, Clone, Get, PartialEq, Eq, Hash)]
|
|
pub struct BuildId(#[get(method = "get", kind = "deref")] String);
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
|
|
pub struct Version {
|
|
numbers: VersionNumbers,
|
|
letter: Option<char>,
|
|
suffixes: VersionSuffixes,
|
|
rev: Option<VersionNumber>,
|
|
build_id: Option<BuildId>,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
pub struct Wildcard;
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
pub enum SlotOperator {
|
|
Eq,
|
|
Star,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
|
|
pub struct SlotName(#[get(method = "name", kind = "deref")] String);
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub enum Slot {
|
|
Wildcard,
|
|
Equal,
|
|
NameEqual {
|
|
primary: SlotName,
|
|
sub: Option<SlotName>,
|
|
},
|
|
Name {
|
|
primary: SlotName,
|
|
sub: Option<SlotName>,
|
|
},
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
pub enum UseDepNegate {
|
|
Minus,
|
|
Exclamation,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
pub enum UseDepSign {
|
|
Enabled,
|
|
Disabled,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
pub enum UseDepCondition {
|
|
Eq,
|
|
Question,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
pub struct Repo(String);
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
|
|
pub struct UseDep {
|
|
negate: Option<UseDepNegate>,
|
|
flag: UseFlag,
|
|
sign: Option<UseDepSign>,
|
|
condition: Option<UseDepCondition>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
|
|
pub struct Cp {
|
|
category: Category,
|
|
name: Name,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Get)]
|
|
pub struct Cpv {
|
|
category: Category,
|
|
name: Name,
|
|
version: Version,
|
|
slot: Option<Slot>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Get, PartialEq, Eq, Hash)]
|
|
pub struct Atom {
|
|
blocker: Option<Blocker>,
|
|
category: Category,
|
|
name: Name,
|
|
version: Option<(VersionOperator, Version, Option<Wildcard>)>,
|
|
slot: Option<Slot>,
|
|
repo: Option<Repo>,
|
|
#[get(kind = "deref")]
|
|
usedeps: Vec<UseDep>,
|
|
}
|
|
|
|
impl Cpv {
|
|
#[must_use]
|
|
pub fn into_cp(self) -> Cp {
|
|
Cp {
|
|
name: self.name,
|
|
category: self.category,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Atom {
|
|
#[must_use]
|
|
pub fn version_operator(&self) -> Option<VersionOperator> {
|
|
self.version.clone().map(|(oper, _, _)| oper)
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn into_cp(self) -> Cp {
|
|
Cp {
|
|
category: self.category,
|
|
name: self.name,
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn into_cpv(self) -> Option<Cpv> {
|
|
match self.version {
|
|
Some((_, version, _)) => Some(Cpv {
|
|
category: self.category,
|
|
name: self.name,
|
|
version,
|
|
slot: self.slot,
|
|
}),
|
|
None => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl VersionNumber {
|
|
#[must_use]
|
|
pub fn cmp_as_ints(&self, other: &Self) -> Ordering {
|
|
let a = self.get().trim_start_matches('0');
|
|
let b = other.get().trim_start_matches('0');
|
|
|
|
a.len().cmp(&b.len()).then_with(|| a.cmp(b))
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn cmp_as_str(&self, other: &Self) -> Ordering {
|
|
if self.get().starts_with('0') || other.get().starts_with('0') {
|
|
let a = self.get().trim_end_matches('0');
|
|
let b = other.get().trim_end_matches('0');
|
|
|
|
a.cmp(b)
|
|
} else {
|
|
self.cmp_as_ints(other)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for BuildId {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl Ord for BuildId {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
// build-id may not start with a zero so we dont need to strip them
|
|
self.get()
|
|
.len()
|
|
.cmp(&other.get().len())
|
|
.then_with(|| self.get().cmp(other.get()))
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for VersionSuffix {
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl Ord for VersionSuffix {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
match &self.kind.cmp(&other.kind) {
|
|
Ordering::Less => Ordering::Less,
|
|
Ordering::Greater => Ordering::Greater,
|
|
Ordering::Equal => match (&self.number, &other.number) {
|
|
(Some(a), Some(b)) => a.cmp_as_ints(b),
|
|
(Some(a), None) if a.get().chars().all(|c| c == '0') => Ordering::Equal,
|
|
(None, Some(b)) if b.get().chars().all(|c| c == '0') => Ordering::Equal,
|
|
(Some(_), None) => Ordering::Greater,
|
|
(None, Some(_)) => Ordering::Less,
|
|
(None, None) => Ordering::Equal,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for VersionSuffixes {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl Ord for VersionSuffixes {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
let mut a = self.get().iter();
|
|
let mut b = other.get().iter();
|
|
|
|
loop {
|
|
match (a.next(), b.next()) {
|
|
(Some(a), Some(b)) => match a.cmp(b) {
|
|
Ordering::Less => break Ordering::Less,
|
|
Ordering::Greater => break Ordering::Greater,
|
|
Ordering::Equal => (),
|
|
},
|
|
(Some(a), None) if matches!(a.kind, VersionSuffixKind::P) => {
|
|
break Ordering::Greater;
|
|
}
|
|
(Some(_), None) => break Ordering::Less,
|
|
(None, Some(b)) if matches!(b.kind, VersionSuffixKind::P) => break Ordering::Less,
|
|
(None, Some(_)) => break Ordering::Greater,
|
|
(None, None) => break Ordering::Equal,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for VersionNumbers {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl Ord for VersionNumbers {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
match self
|
|
.get()
|
|
.first()
|
|
.unwrap()
|
|
.cmp_as_ints(other.get().first().unwrap())
|
|
{
|
|
Ordering::Less => Ordering::Less,
|
|
Ordering::Greater => Ordering::Greater,
|
|
Ordering::Equal => {
|
|
let mut a = self.get().iter().skip(1);
|
|
let mut b = other.get().iter().skip(1);
|
|
|
|
loop {
|
|
match (a.next(), b.next()) {
|
|
(Some(a), Some(b)) => match a.cmp_as_str(b) {
|
|
Ordering::Less => break Ordering::Less,
|
|
Ordering::Greater => break Ordering::Greater,
|
|
Ordering::Equal => (),
|
|
},
|
|
|
|
(Some(_), None) => break Ordering::Greater,
|
|
(None, Some(_)) => break Ordering::Less,
|
|
(None, None) => break Ordering::Equal,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for Version {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl Ord for Version {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
match self.numbers.cmp(&other.numbers) {
|
|
Ordering::Less => return Ordering::Less,
|
|
Ordering::Greater => return Ordering::Greater,
|
|
Ordering::Equal => (),
|
|
}
|
|
|
|
match (self.letter, other.letter) {
|
|
(Some(a), Some(b)) if a < b => return Ordering::Less,
|
|
(Some(a), Some(b)) if a > b => return Ordering::Greater,
|
|
(Some(a), Some(b)) if a == b => (),
|
|
(Some(_), None) => return Ordering::Greater,
|
|
(None, Some(_)) => return Ordering::Less,
|
|
(None, None) => (),
|
|
_ => unreachable!(),
|
|
}
|
|
|
|
match self.suffixes.cmp(&other.suffixes) {
|
|
Ordering::Less => return Ordering::Less,
|
|
Ordering::Greater => return Ordering::Greater,
|
|
Ordering::Equal => (),
|
|
}
|
|
|
|
match (&self.rev, &other.rev) {
|
|
(Some(a), Some(b)) => match a.cmp_as_ints(b) {
|
|
Ordering::Less => return Ordering::Less,
|
|
Ordering::Greater => return Ordering::Greater,
|
|
Ordering::Equal => (),
|
|
},
|
|
(Some(a), None) if a.get().chars().all(|c| c == '0') => (),
|
|
(Some(_), None) => return Ordering::Greater,
|
|
(None, Some(b)) if b.get().chars().all(|c| c == '0') => (),
|
|
(None, Some(_)) => return Ordering::Less,
|
|
(None, None) => (),
|
|
}
|
|
|
|
match (&self.build_id, &other.build_id) {
|
|
(Some(a), Some(b)) => a.cmp(b),
|
|
(Some(_), None) => Ordering::Greater,
|
|
(None, Some(_)) => Ordering::Less,
|
|
(None, None) => Ordering::Equal,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for Cpv {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
if self.category == other.category && self.name == other.name {
|
|
Some(self.version.cmp(&other.version))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Blocker {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Weak => write!(f, "!"),
|
|
Self::Strong => write!(f, "!!"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for VersionOperator {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Lt => write!(f, "<"),
|
|
Self::Gt => write!(f, ">"),
|
|
Self::Eq => write!(f, "="),
|
|
Self::LtEq => write!(f, "<="),
|
|
Self::GtEq => write!(f, ">="),
|
|
Self::Roughly => write!(f, "~"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Category {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Name {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for VersionNumber {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for BuildId {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self.get())
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for VersionSuffixKind {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Alpha => write!(f, "alpha"),
|
|
Self::Beta => write!(f, "beta"),
|
|
Self::Pre => write!(f, "pre"),
|
|
Self::Rc => write!(f, "rc"),
|
|
Self::P => write!(f, "p"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for VersionSuffix {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}", self.kind)?;
|
|
|
|
if let Some(number) = self.number.as_ref() {
|
|
write!(f, "{number}")?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Version {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let numbers = self
|
|
.numbers
|
|
.get()
|
|
.iter()
|
|
.map(VersionNumber::get)
|
|
.intersperse(".")
|
|
.collect::<String>();
|
|
|
|
let suffixes = self
|
|
.suffixes
|
|
.get()
|
|
.iter()
|
|
.map(VersionSuffix::to_string)
|
|
.intersperse("_".to_string())
|
|
.collect::<String>();
|
|
|
|
write!(f, "{numbers}")?;
|
|
|
|
if let Some(letter) = self.letter {
|
|
write!(f, "{letter}")?;
|
|
}
|
|
|
|
if !suffixes.is_empty() {
|
|
write!(f, "_{suffixes}")?;
|
|
}
|
|
|
|
if let Some(rev) = self.rev.as_ref() {
|
|
write!(f, "-r{rev}")?;
|
|
}
|
|
|
|
if let Some(build_id) = self.build_id.as_ref() {
|
|
write!(f, "-{build_id}")?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for SlotOperator {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Eq => write!(f, "="),
|
|
Self::Star => write!(f, "*"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for SlotName {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Slot {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Wildcard => write!(f, "*"),
|
|
Self::Equal => {
|
|
write!(f, "=")
|
|
}
|
|
Self::NameEqual { primary, sub } => {
|
|
write!(f, "{primary}")?;
|
|
|
|
if let Some(sub) = sub {
|
|
write!(f, "/{sub}")?;
|
|
}
|
|
|
|
write!(f, "=")
|
|
}
|
|
Self::Name { primary, sub } => {
|
|
write!(f, "{primary}")?;
|
|
|
|
if let Some(sub) = sub {
|
|
write!(f, "/{sub}")?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for UseDepNegate {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Minus => write!(f, "-"),
|
|
Self::Exclamation => write!(f, "!"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for UseDepSign {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Enabled => write!(f, "(+)"),
|
|
Self::Disabled => write!(f, "(-)"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for UseDepCondition {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Eq => write!(f, "="),
|
|
Self::Question => write!(f, "?"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for UseDep {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
if let Some(negate) = self.negate.as_ref() {
|
|
write!(f, "{negate}")?;
|
|
}
|
|
|
|
write!(f, "{}", self.flag)?;
|
|
|
|
if let Some(sign) = self.sign.as_ref() {
|
|
write!(f, "{sign}")?;
|
|
}
|
|
|
|
if let Some(condition) = self.condition.as_ref() {
|
|
write!(f, "{condition}")?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Cp {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}/{}", &self.category, &self.name)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Cpv {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}/{}-{}", &self.category, &self.name, &self.version)?;
|
|
|
|
if let Some(slot) = self.slot.as_ref() {
|
|
write!(f, ":{slot}")?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Atom {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
if let Some(blocker) = self.blocker.as_ref() {
|
|
write!(f, "{blocker}")?;
|
|
}
|
|
|
|
if let Some(version_operator) = self.version_operator().as_ref() {
|
|
write!(f, "{version_operator}")?;
|
|
}
|
|
|
|
write!(f, "{}", self.category)?;
|
|
write!(f, "/")?;
|
|
write!(f, "{}", self.name)?;
|
|
|
|
if let Some((_, version, None)) = self.version() {
|
|
write!(f, "-{version}")?;
|
|
} else if let Some((_, version, Some(_))) = self.version() {
|
|
write!(f, "-{version}*")?;
|
|
}
|
|
|
|
if let Some(slot) = self.slot.as_ref() {
|
|
write!(f, ":{slot}")?;
|
|
}
|
|
|
|
let usedeps = self
|
|
.usedeps
|
|
.iter()
|
|
.map(UseDep::to_string)
|
|
.intersperse(",".to_string())
|
|
.collect::<String>();
|
|
|
|
if !usedeps.is_empty() {
|
|
write!(f, "[{usedeps}]")?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use mon::{Parser, input::InputIter};
|
|
|
|
use super::*;
|
|
|
|
use crate::Parseable;
|
|
|
|
macro_rules! assert_eq_display {
|
|
($a:expr, $b:expr) => {
|
|
if $a != $b {
|
|
panic!("{} != {}", $a, $b);
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! assert_cmp_display {
|
|
($a:expr, $b:expr, $ordering:expr) => {
|
|
if $a.cmp(&$b) != $ordering {
|
|
panic!("{} ~ {} != {:?}", $a, $b, $ordering)
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! assert_partial_cmp_display {
|
|
($a:expr, $b:expr, $ordering:expr) => {
|
|
if $a.partial_cmp(&$b) != $ordering {
|
|
panic!("{} ~ {} != {:?}", $a, $b, $ordering)
|
|
}
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn test_version_display() {
|
|
let s = "1.0.0_alpha1_beta1-r1";
|
|
let version = Version::parser().parse_finished(InputIter::new(s)).unwrap();
|
|
|
|
assert_eq!(version.to_string().as_str(), s);
|
|
}
|
|
|
|
#[test]
|
|
fn test_display_atom() {
|
|
let s = "!!>=foo/bar-1.0.0v_alpha1_beta1-r1:slot/sub=[a,b,c]";
|
|
let atom = Atom::parser().parse_finished(InputIter::new(s)).unwrap();
|
|
|
|
assert_eq!(atom.to_string().as_str(), s);
|
|
}
|
|
|
|
#[test]
|
|
fn test_version_cmp() {
|
|
let versions = [
|
|
("1.0.1", "1.0", Ordering::Greater),
|
|
("1.0.0", "1.0.0_alpha", Ordering::Greater),
|
|
("1.0.0_alpha", "1.0.0_alpha_p", Ordering::Less),
|
|
("1.0.0-r0", "1.0.0", Ordering::Equal),
|
|
("1.0.0-r0000", "1.0.0", Ordering::Equal),
|
|
("1.0.0-r1-1", "1.0.0-r1-2", Ordering::Less),
|
|
];
|
|
|
|
for (a, b, ordering) in versions.iter().map(|(a, b, ordering)| {
|
|
(
|
|
Version::parser().parse_finished(InputIter::new(a)).unwrap(),
|
|
Version::parser().parse_finished(InputIter::new(b)).unwrap(),
|
|
ordering,
|
|
)
|
|
}) {
|
|
assert_cmp_display!(a, b, *ordering);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_cpv_eq() {
|
|
let cpvs = [
|
|
("foo/bar-1", "foo/bar-1", Some(Ordering::Equal)),
|
|
("foo/baz-1", "foo/bar-1", None),
|
|
];
|
|
|
|
for (a, b, ordering) in cpvs.iter().copied().map(|(a, b, ordering)| {
|
|
(
|
|
Cpv::parser().parse_finished(InputIter::new(a)).unwrap(),
|
|
Cpv::parser().parse_finished(InputIter::new(b)).unwrap(),
|
|
ordering,
|
|
)
|
|
}) {
|
|
assert_partial_cmp_display!(a, b, ordering);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_version_cmp_letter() {
|
|
let a = Version::parser()
|
|
.parse_finished(InputIter::new("1.0.0"))
|
|
.unwrap();
|
|
let b = Version::parser()
|
|
.parse_finished(InputIter::new("1.0.0a"))
|
|
.unwrap();
|
|
|
|
assert_cmp_display!(a, b, Ordering::Less);
|
|
}
|
|
|
|
#[test]
|
|
fn test_version_cmp_where_b_has_leading_zeros() {
|
|
let a = Version::parser()
|
|
.parse_finished(InputIter::new("1.2"))
|
|
.unwrap();
|
|
let b = Version::parser()
|
|
.parse_finished(InputIter::new("1.054"))
|
|
.unwrap();
|
|
|
|
assert_cmp_display!(a, b, Ordering::Greater);
|
|
}
|
|
|
|
#[test]
|
|
fn test_version_has_more_zeros() {
|
|
let a = Version::parser()
|
|
.parse_finished(InputIter::new("1.0.0"))
|
|
.unwrap();
|
|
let b = Version::parser()
|
|
.parse_finished(InputIter::new("1.0"))
|
|
.unwrap();
|
|
|
|
assert_cmp_display!(a, b, Ordering::Greater);
|
|
}
|
|
|
|
#[test]
|
|
fn test_fuzzer_cases() {
|
|
let control = Version::parser()
|
|
.parse_finished(InputIter::new("1.2.0a_alpha1_beta2-r1-8"))
|
|
.unwrap();
|
|
|
|
for (version_str, expected) in [("1.2.0", Ordering::Greater)] {
|
|
let version = Version::parser()
|
|
.parse_finished(InputIter::new(version_str))
|
|
.unwrap();
|
|
|
|
assert_cmp_display!(control, version, expected);
|
|
}
|
|
}
|
|
}
|