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 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}