semver/
impls.rs

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(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(self.cmp(rhs))
42    }
43}
44
45impl PartialOrd for BuildMetadata {
46    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
47        Some(self.cmp(rhs))
48    }
49}
50
51impl Ord for Prerelease {
52    fn cmp(&self, rhs: &Self) -> Ordering {
53        if self.identifier.ptr_eq(&rhs.identifier) {
54            return Ordering::Equal;
55        }
56
57        match self.is_empty() {
58            // A real release compares greater than prerelease.
59            true => return Ordering::Greater,
60            // Prerelease compares less than the real release.
61            false if rhs.is_empty() => return Ordering::Less,
62            false => {}
63        }
64
65        let lhs = self.as_str().split('.');
66        let mut rhs = rhs.as_str().split('.');
67
68        for lhs in lhs {
69            let rhs = match rhs.next() {
70                // Spec: "A larger set of pre-release fields has a higher
71                // precedence than a smaller set, if all of the preceding
72                // identifiers are equal."
73                None => return Ordering::Greater,
74                Some(rhs) => rhs,
75            };
76
77            let string_cmp = || Ord::cmp(lhs, rhs);
78            let is_ascii_digit = |b: u8| b.is_ascii_digit();
79            let ordering = match (
80                lhs.bytes().all(is_ascii_digit),
81                rhs.bytes().all(is_ascii_digit),
82            ) {
83                // Respect numeric ordering, for example 99 < 100. Spec says:
84                // "Identifiers consisting of only digits are compared
85                // numerically."
86                (true, true) => Ord::cmp(&lhs.len(), &rhs.len()).then_with(string_cmp),
87                // Spec: "Numeric identifiers always have lower precedence than
88                // non-numeric identifiers."
89                (true, false) => return Ordering::Less,
90                (false, true) => return Ordering::Greater,
91                // Spec: "Identifiers with letters or hyphens are compared
92                // lexically in ASCII sort order."
93                (false, false) => string_cmp(),
94            };
95
96            if ordering != Ordering::Equal {
97                return ordering;
98            }
99        }
100
101        if rhs.next().is_none() {
102            Ordering::Equal
103        } else {
104            Ordering::Less
105        }
106    }
107}
108
109impl Ord for BuildMetadata {
110    fn cmp(&self, rhs: &Self) -> Ordering {
111        if self.identifier.ptr_eq(&rhs.identifier) {
112            return Ordering::Equal;
113        }
114
115        let lhs = self.as_str().split('.');
116        let mut rhs = rhs.as_str().split('.');
117
118        for lhs in lhs {
119            let rhs = match rhs.next() {
120                None => return Ordering::Greater,
121                Some(rhs) => rhs,
122            };
123
124            let is_ascii_digit = |b: u8| b.is_ascii_digit();
125            let ordering = match (
126                lhs.bytes().all(is_ascii_digit),
127                rhs.bytes().all(is_ascii_digit),
128            ) {
129                (true, true) => {
130                    // 0 < 00 < 1 < 01 < 001 < 2 < 02 < 002 < 10
131                    let lhval = lhs.trim_start_matches('0');
132                    let rhval = rhs.trim_start_matches('0');
133                    Ord::cmp(&lhval.len(), &rhval.len())
134                        .then_with(|| Ord::cmp(lhval, rhval))
135                        .then_with(|| Ord::cmp(&lhs.len(), &rhs.len()))
136                }
137                (true, false) => return Ordering::Less,
138                (false, true) => return Ordering::Greater,
139                (false, false) => Ord::cmp(lhs, rhs),
140            };
141
142            if ordering != Ordering::Equal {
143                return ordering;
144            }
145        }
146
147        if rhs.next().is_none() {
148            Ordering::Equal
149        } else {
150            Ordering::Less
151        }
152    }
153}
154
155impl FromIterator<Comparator> for VersionReq {
156    fn from_iter<I>(iter: I) -> Self
157    where
158        I: IntoIterator<Item = Comparator>,
159    {
160        let comparators = Vec::from_iter(iter);
161        VersionReq { comparators }
162    }
163}