1use crate::identifier::Identifier;
2use crate::{BuildMetadata, Comparator, Prerelease, VersionReq};
3use alloc::vec::Vec;
4use core::cmp::Ordering;
5use core::hash::{Hash, Hasher};
6use core::ops::Deref;
7
8impl Default for Identifier {
9 fn default() -> Self {
10 Identifier::empty()
11 }
12}
13
14impl Eq for Identifier {}
15
16impl Hash for Identifier {
17 fn hash<H: Hasher>(&self, hasher: &mut H) {
18 self.as_str().hash(hasher);
19 }
20}
21
22impl Deref for Prerelease {
23 type Target = str;
24
25 fn deref(&self) -> &Self::Target {
26 self.identifier.as_str()
27 }
28}
29
30impl Deref for BuildMetadata {
31 type Target = str;
32
33 fn deref(&self) -> &Self::Target {
34 self.identifier.as_str()
35 }
36}
37
38impl PartialOrd for Prerelease {
39 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
40 Some(self.cmp(rhs))
41 }
42}
43
44impl PartialOrd for BuildMetadata {
45 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
46 Some(self.cmp(rhs))
47 }
48}
49
50impl Ord for Prerelease {
51 fn cmp(&self, rhs: &Self) -> Ordering {
52 if self.identifier.ptr_eq(&rhs.identifier) {
53 return Ordering::Equal;
54 }
55
56 match self.is_empty() {
57 true => return Ordering::Greater,
59 false if rhs.is_empty() => return Ordering::Less,
61 false => {}
62 }
63
64 let lhs = self.as_str().split('.');
65 let mut rhs = rhs.as_str().split('.');
66
67 for lhs in lhs {
68 let Some(rhs) = rhs.next() else {
69 return Ordering::Greater;
73 };
74
75 let string_cmp = || Ord::cmp(lhs, rhs);
76 let is_ascii_digit = |b: u8| b.is_ascii_digit();
77 let ordering = match (
78 lhs.bytes().all(is_ascii_digit),
79 rhs.bytes().all(is_ascii_digit),
80 ) {
81 (true, true) => Ord::cmp(&lhs.len(), &rhs.len()).then_with(string_cmp),
85 (true, false) => return Ordering::Less,
88 (false, true) => return Ordering::Greater,
89 (false, false) => string_cmp(),
92 };
93
94 if ordering != Ordering::Equal {
95 return ordering;
96 }
97 }
98
99 if rhs.next().is_none() {
100 Ordering::Equal
101 } else {
102 Ordering::Less
103 }
104 }
105}
106
107impl Ord for BuildMetadata {
108 fn cmp(&self, rhs: &Self) -> Ordering {
109 if self.identifier.ptr_eq(&rhs.identifier) {
110 return Ordering::Equal;
111 }
112
113 let lhs = self.as_str().split('.');
114 let mut rhs = rhs.as_str().split('.');
115
116 for lhs in lhs {
117 let Some(rhs) = rhs.next() else {
118 return Ordering::Greater;
119 };
120
121 let is_ascii_digit = |b: u8| b.is_ascii_digit();
122 let ordering = match (
123 lhs.bytes().all(is_ascii_digit),
124 rhs.bytes().all(is_ascii_digit),
125 ) {
126 (true, true) => {
127 let lhval = lhs.trim_start_matches('0');
129 let rhval = rhs.trim_start_matches('0');
130 Ord::cmp(&lhval.len(), &rhval.len())
131 .then_with(|| Ord::cmp(lhval, rhval))
132 .then_with(|| Ord::cmp(&lhs.len(), &rhs.len()))
133 }
134 (true, false) => return Ordering::Less,
135 (false, true) => return Ordering::Greater,
136 (false, false) => Ord::cmp(lhs, rhs),
137 };
138
139 if ordering != Ordering::Equal {
140 return ordering;
141 }
142 }
143
144 if rhs.next().is_none() {
145 Ordering::Equal
146 } else {
147 Ordering::Less
148 }
149 }
150}
151
152impl FromIterator<Comparator> for VersionReq {
153 fn from_iter<I>(iter: I) -> Self
154 where
155 I: IntoIterator<Item = Comparator>,
156 {
157 let comparators = Vec::from_iter(iter);
158 VersionReq { comparators }
159 }
160}