Source code
Revision control
Copy as Markdown
Other Tools
use crate::backport::*;
use crate::identifier::Identifier;
use crate::{BuildMetadata, Comparator, Prerelease, VersionReq};
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::iter::FromIterator;
use core::ops::Deref;
impl Default for Identifier {
fn default() -> Self {
Identifier::empty()
}
}
impl Eq for Identifier {}
impl Hash for Identifier {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.as_str().hash(hasher);
}
}
impl Deref for Prerelease {
type Target = str;
fn deref(&self) -> &Self::Target {
self.identifier.as_str()
}
}
impl Deref for BuildMetadata {
type Target = str;
fn deref(&self) -> &Self::Target {
self.identifier.as_str()
}
}
impl PartialOrd for Prerelease {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
Some(Ord::cmp(self, rhs))
}
}
impl PartialOrd for BuildMetadata {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
Some(Ord::cmp(self, rhs))
}
}
impl Ord for Prerelease {
fn cmp(&self, rhs: &Self) -> Ordering {
match self.is_empty() {
true if rhs.is_empty() => return Ordering::Equal,
// A real release compares greater than prerelease.
true => return Ordering::Greater,
// Prerelease compares less than the real release.
false if rhs.is_empty() => return Ordering::Less,
false => {}
}
let lhs = self.as_str().split('.');
let mut rhs = rhs.as_str().split('.');
for lhs in lhs {
let rhs = match rhs.next() {
// Spec: "A larger set of pre-release fields has a higher
// precedence than a smaller set, if all of the preceding
// identifiers are equal."
None => return Ordering::Greater,
Some(rhs) => rhs,
};
let string_cmp = || Ord::cmp(lhs, rhs);
let is_ascii_digit = |b: u8| b.is_ascii_digit();
let ordering = match (
lhs.bytes().all(is_ascii_digit),
rhs.bytes().all(is_ascii_digit),
) {
// Respect numeric ordering, for example 99 < 100. Spec says:
// "Identifiers consisting of only digits are compared
// numerically."
(true, true) => Ord::cmp(&lhs.len(), &rhs.len()).then_with(string_cmp),
// Spec: "Numeric identifiers always have lower precedence than
// non-numeric identifiers."
(true, false) => return Ordering::Less,
(false, true) => return Ordering::Greater,
// Spec: "Identifiers with letters or hyphens are compared
// lexically in ASCII sort order."
(false, false) => string_cmp(),
};
if ordering != Ordering::Equal {
return ordering;
}
}
if rhs.next().is_none() {
Ordering::Equal
} else {
Ordering::Less
}
}
}
impl Ord for BuildMetadata {
fn cmp(&self, rhs: &Self) -> Ordering {
let lhs = self.as_str().split('.');
let mut rhs = rhs.as_str().split('.');
for lhs in lhs {
let rhs = match rhs.next() {
None => return Ordering::Greater,
Some(rhs) => rhs,
};
let is_ascii_digit = |b: u8| b.is_ascii_digit();
let ordering = match (
lhs.bytes().all(is_ascii_digit),
rhs.bytes().all(is_ascii_digit),
) {
(true, true) => {
// 0 < 00 < 1 < 01 < 001 < 2 < 02 < 002 < 10
let lhval = lhs.trim_start_matches('0');
let rhval = rhs.trim_start_matches('0');
Ord::cmp(&lhval.len(), &rhval.len())
.then_with(|| Ord::cmp(lhval, rhval))
.then_with(|| Ord::cmp(&lhs.len(), &rhs.len()))
}
(true, false) => return Ordering::Less,
(false, true) => return Ordering::Greater,
(false, false) => Ord::cmp(lhs, rhs),
};
if ordering != Ordering::Equal {
return ordering;
}
}
if rhs.next().is_none() {
Ordering::Equal
} else {
Ordering::Less
}
}
}
impl FromIterator<Comparator> for VersionReq {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = Comparator>,
{
let comparators = Vec::from_iter(iter);
VersionReq { comparators }
}
}