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(&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 { Some(Ord::cmp(self, rhs)) } } impl PartialOrd for BuildMetadata { fn partial_cmp(&self, rhs: &Self) -> Option { 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 for VersionReq { fn from_iter(iter: I) -> Self where I: IntoIterator, { let comparators = Vec::from_iter(iter); VersionReq { comparators } } }