semver/
eval.rs

1use crate::{Comparator, Op, Version, VersionReq};
2
3pub(crate) fn matches_req(req: &VersionReq, ver: &Version) -> bool {
4    for cmp in &req.comparators {
5        if !matches_impl(cmp, ver) {
6            return false;
7        }
8    }
9
10    if ver.pre.is_empty() {
11        return true;
12    }
13
14    // If a version has a prerelease tag (for example, 1.2.3-alpha.3) then it
15    // will only be allowed to satisfy req if at least one comparator with the
16    // same major.minor.patch also has a prerelease tag.
17    for cmp in &req.comparators {
18        if pre_is_compatible(cmp, ver) {
19            return true;
20        }
21    }
22
23    false
24}
25
26pub(crate) fn matches_comparator(cmp: &Comparator, ver: &Version) -> bool {
27    matches_impl(cmp, ver) && (ver.pre.is_empty() || pre_is_compatible(cmp, ver))
28}
29
30fn matches_impl(cmp: &Comparator, ver: &Version) -> bool {
31    match cmp.op {
32        Op::Exact | Op::Wildcard => matches_exact(cmp, ver),
33        Op::Greater => matches_greater(cmp, ver),
34        Op::GreaterEq => matches_exact(cmp, ver) || matches_greater(cmp, ver),
35        Op::Less => matches_less(cmp, ver),
36        Op::LessEq => matches_exact(cmp, ver) || matches_less(cmp, ver),
37        Op::Tilde => matches_tilde(cmp, ver),
38        Op::Caret => matches_caret(cmp, ver),
39        #[cfg(no_non_exhaustive)]
40        Op::__NonExhaustive => unreachable!(),
41    }
42}
43
44fn matches_exact(cmp: &Comparator, ver: &Version) -> bool {
45    if ver.major != cmp.major {
46        return false;
47    }
48
49    if let Some(minor) = cmp.minor {
50        if ver.minor != minor {
51            return false;
52        }
53    }
54
55    if let Some(patch) = cmp.patch {
56        if ver.patch != patch {
57            return false;
58        }
59    }
60
61    ver.pre == cmp.pre
62}
63
64fn matches_greater(cmp: &Comparator, ver: &Version) -> bool {
65    if ver.major != cmp.major {
66        return ver.major > cmp.major;
67    }
68
69    match cmp.minor {
70        None => return false,
71        Some(minor) => {
72            if ver.minor != minor {
73                return ver.minor > minor;
74            }
75        }
76    }
77
78    match cmp.patch {
79        None => return false,
80        Some(patch) => {
81            if ver.patch != patch {
82                return ver.patch > patch;
83            }
84        }
85    }
86
87    ver.pre > cmp.pre
88}
89
90fn matches_less(cmp: &Comparator, ver: &Version) -> bool {
91    if ver.major != cmp.major {
92        return ver.major < cmp.major;
93    }
94
95    match cmp.minor {
96        None => return false,
97        Some(minor) => {
98            if ver.minor != minor {
99                return ver.minor < minor;
100            }
101        }
102    }
103
104    match cmp.patch {
105        None => return false,
106        Some(patch) => {
107            if ver.patch != patch {
108                return ver.patch < patch;
109            }
110        }
111    }
112
113    ver.pre < cmp.pre
114}
115
116fn matches_tilde(cmp: &Comparator, ver: &Version) -> bool {
117    if ver.major != cmp.major {
118        return false;
119    }
120
121    if let Some(minor) = cmp.minor {
122        if ver.minor != minor {
123            return false;
124        }
125    }
126
127    if let Some(patch) = cmp.patch {
128        if ver.patch != patch {
129            return ver.patch > patch;
130        }
131    }
132
133    ver.pre >= cmp.pre
134}
135
136fn matches_caret(cmp: &Comparator, ver: &Version) -> bool {
137    if ver.major != cmp.major {
138        return false;
139    }
140
141    let minor = match cmp.minor {
142        None => return true,
143        Some(minor) => minor,
144    };
145
146    let patch = match cmp.patch {
147        None => {
148            if cmp.major > 0 {
149                return ver.minor >= minor;
150            } else {
151                return ver.minor == minor;
152            }
153        }
154        Some(patch) => patch,
155    };
156
157    if cmp.major > 0 {
158        if ver.minor != minor {
159            return ver.minor > minor;
160        } else if ver.patch != patch {
161            return ver.patch > patch;
162        }
163    } else if minor > 0 {
164        if ver.minor != minor {
165            return false;
166        } else if ver.patch != patch {
167            return ver.patch > patch;
168        }
169    } else if ver.minor != minor || ver.patch != patch {
170        return false;
171    }
172
173    ver.pre >= cmp.pre
174}
175
176fn pre_is_compatible(cmp: &Comparator, ver: &Version) -> bool {
177    cmp.major == ver.major
178        && cmp.minor == Some(ver.minor)
179        && cmp.patch == Some(ver.patch)
180        && !cmp.pre.is_empty()
181}