1use crate::backport::*;
2use crate::identifier::Identifier;
3use crate::{BuildMetadata, Comparator, Prerelease, VersionReq};
4use core::cmp::Ordering;
5use core::hash::{Hash, Hasher};
6use core::iter::FromIterator;
7use core::ops::Deref;
8
9impl Default for Identifier {
10 fn default() -> Self {
11 Identifier::empty()
12 }
13}
14
15impl Eq for Identifier {}
16
17impl Hash for Identifier {
18 fn hash<H: Hasher>(&self, hasher: &mut H) {
19 self.as_str().hash(state:hasher);
20 }
21}
22
23impl Deref for Prerelease {
24 type Target = str;
25
26 fn deref(&self) -> &Self::Target {
27 self.identifier.as_str()
28 }
29}
30
31impl Deref for BuildMetadata {
32 type Target = str;
33
34 fn deref(&self) -> &Self::Target {
35 self.identifier.as_str()
36 }
37}
38
39impl PartialOrd for Prerelease {
40 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
41 Some(Ord::cmp(self, other:rhs))
42 }
43}
44
45impl PartialOrd for BuildMetadata {
46 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
47 Some(Ord::cmp(self, other:rhs))
48 }
49}
50
51impl Ord for Prerelease {
52 fn cmp(&self, rhs: &Self) -> Ordering {
53 match self.is_empty() {
54 true if rhs.is_empty() => return Ordering::Equal,
55 // A real release compares greater than prerelease.
56 true => return Ordering::Greater,
57 // Prerelease compares less than the real release.
58 false if rhs.is_empty() => return Ordering::Less,
59 false => {}
60 }
61
62 let lhs = self.as_str().split('.');
63 let mut rhs = rhs.as_str().split('.');
64
65 for lhs in lhs {
66 let rhs = match rhs.next() {
67 // Spec: "A larger set of pre-release fields has a higher
68 // precedence than a smaller set, if all of the preceding
69 // identifiers are equal."
70 None => return Ordering::Greater,
71 Some(rhs) => rhs,
72 };
73
74 let string_cmp = || Ord::cmp(lhs, rhs);
75 let is_ascii_digit = |b: u8| b.is_ascii_digit();
76 let ordering = match (
77 lhs.bytes().all(is_ascii_digit),
78 rhs.bytes().all(is_ascii_digit),
79 ) {
80 // Respect numeric ordering, for example 99 < 100. Spec says:
81 // "Identifiers consisting of only digits are compared
82 // numerically."
83 (true, true) => Ord::cmp(&lhs.len(), &rhs.len()).then_with(string_cmp),
84 // Spec: "Numeric identifiers always have lower precedence than
85 // non-numeric identifiers."
86 (true, false) => return Ordering::Less,
87 (false, true) => return Ordering::Greater,
88 // Spec: "Identifiers with letters or hyphens are compared
89 // lexically in ASCII sort order."
90 (false, false) => string_cmp(),
91 };
92
93 if ordering != Ordering::Equal {
94 return ordering;
95 }
96 }
97
98 if rhs.next().is_none() {
99 Ordering::Equal
100 } else {
101 Ordering::Less
102 }
103 }
104}
105
106impl Ord for BuildMetadata {
107 fn cmp(&self, rhs: &Self) -> Ordering {
108 let lhs = self.as_str().split('.');
109 let mut rhs = rhs.as_str().split('.');
110
111 for lhs in lhs {
112 let rhs = match rhs.next() {
113 None => return Ordering::Greater,
114 Some(rhs) => rhs,
115 };
116
117 let is_ascii_digit = |b: u8| b.is_ascii_digit();
118 let ordering = match (
119 lhs.bytes().all(is_ascii_digit),
120 rhs.bytes().all(is_ascii_digit),
121 ) {
122 (true, true) => {
123 // 0 < 00 < 1 < 01 < 001 < 2 < 02 < 002 < 10
124 let lhval = lhs.trim_start_matches('0');
125 let rhval = rhs.trim_start_matches('0');
126 Ord::cmp(&lhval.len(), &rhval.len())
127 .then_with(|| Ord::cmp(lhval, rhval))
128 .then_with(|| Ord::cmp(&lhs.len(), &rhs.len()))
129 }
130 (true, false) => return Ordering::Less,
131 (false, true) => return Ordering::Greater,
132 (false, false) => Ord::cmp(lhs, rhs),
133 };
134
135 if ordering != Ordering::Equal {
136 return ordering;
137 }
138 }
139
140 if rhs.next().is_none() {
141 Ordering::Equal
142 } else {
143 Ordering::Less
144 }
145 }
146}
147
148impl FromIterator<Comparator> for VersionReq {
149 fn from_iter<I>(iter: I) -> Self
150 where
151 I: IntoIterator<Item = Comparator>,
152 {
153 let comparators: Vec = Vec::from_iter(iter);
154 VersionReq { comparators }
155 }
156}
157