Coverage Report

Created: 2025-10-10 07:11

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