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    }
40}
41
42fn matches_exact(cmp: &Comparator, ver: &Version) -> bool {
43    if ver.major != cmp.major {
44        return false;
45    }
46
47    if let Some(minor) = cmp.minor {
48        if ver.minor != minor {
49            return false;
50        }
51    }
52
53    if let Some(patch) = cmp.patch {
54        if ver.patch != patch {
55            return false;
56        }
57    }
58
59    ver.pre == cmp.pre
60}
61
62fn matches_greater(cmp: &Comparator, ver: &Version) -> bool {
63    if ver.major != cmp.major {
64        return ver.major > cmp.major;
65    }
66
67    match cmp.minor {
68        None => return false,
69        Some(minor) => {
70            if ver.minor != minor {
71                return ver.minor > minor;
72            }
73        }
74    }
75
76    match cmp.patch {
77        None => return false,
78        Some(patch) => {
79            if ver.patch != patch {
80                return ver.patch > patch;
81            }
82        }
83    }
84
85    ver.pre > cmp.pre
86}
87
88fn matches_less(cmp: &Comparator, ver: &Version) -> bool {
89    if ver.major != cmp.major {
90        return ver.major < cmp.major;
91    }
92
93    match cmp.minor {
94        None => return false,
95        Some(minor) => {
96            if ver.minor != minor {
97                return ver.minor < minor;
98            }
99        }
100    }
101
102    match cmp.patch {
103        None => return false,
104        Some(patch) => {
105            if ver.patch != patch {
106                return ver.patch < patch;
107            }
108        }
109    }
110
111    ver.pre < cmp.pre
112}
113
114fn matches_tilde(cmp: &Comparator, ver: &Version) -> bool {
115    if ver.major != cmp.major {
116        return false;
117    }
118
119    if let Some(minor) = cmp.minor {
120        if ver.minor != minor {
121            return false;
122        }
123    }
124
125    if let Some(patch) = cmp.patch {
126        if ver.patch != patch {
127            return ver.patch > patch;
128        }
129    }
130
131    ver.pre >= cmp.pre
132}
133
134fn matches_caret(cmp: &Comparator, ver: &Version) -> bool {
135    if ver.major != cmp.major {
136        return false;
137    }
138
139    let minor = match cmp.minor {
140        None => return true,
141        Some(minor) => minor,
142    };
143
144    let patch = match cmp.patch {
145        None => {
146            if cmp.major > 0 {
147                return ver.minor >= minor;
148            } else {
149                return ver.minor == minor;
150            }
151        }
152        Some(patch) => patch,
153    };
154
155    if cmp.major > 0 {
156        if ver.minor != minor {
157            return ver.minor > minor;
158        } else if ver.patch != patch {
159            return ver.patch > patch;
160        }
161    } else if minor > 0 {
162        if ver.minor != minor {
163            return false;
164        } else if ver.patch != patch {
165            return ver.patch > patch;
166        }
167    } else if ver.minor != minor || ver.patch != patch {
168        return false;
169    }
170
171    ver.pre >= cmp.pre
172}
173
174fn pre_is_compatible(cmp: &Comparator, ver: &Version) -> bool {
175    cmp.major == ver.major
176        && cmp.minor == Some(ver.minor)
177        && cmp.patch == Some(ver.patch)
178        && !cmp.pre.is_empty()
179}