1use crate::{Comparator, Op, Version, VersionReq};
2
3pub(crate) fn matches_req(req: &VersionReq, ver: &Version) -> bool {
4 for cmp: &Comparator 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: &Comparator 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: u64) = cmp.minor {
50 if ver.minor != minor {
51 return false;
52 }
53 }
54
55 if let Some(patch: u64) = 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: u64) = cmp.minor {
122 if ver.minor != minor {
123 return false;
124 }
125 }
126
127 if let Some(patch: u64) = 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}
182