1use std::{cmp::Ordering, fmt::Display};
2
3use crate::parser::version::version_scheme;
4
5#[cfg(test)]
6mod tests;
7
8#[derive(Debug, PartialEq, Clone, Copy)]
10pub enum Comparison {
11 LessThan,
12 LessThanOrEqual,
13 NotEqual,
14 Equal,
15 GreaterThanOrEqual,
16 GreaterThan,
17 CompatibleRelease,
18 ArbitraryEqual,
19}
20
21impl TryFrom<&str> for Comparison {
22 type Error = ();
23
24 fn try_from(value: &str) -> Result<Self, Self::Error> {
25 match value {
26 "<" => Ok(Self::LessThan),
27 "<=" => Ok(Self::LessThanOrEqual),
28 "!=" => Ok(Self::NotEqual),
29 "==" => Ok(Self::Equal),
30 ">=" => Ok(Self::GreaterThanOrEqual),
31 ">" => Ok(Self::GreaterThan),
32 "~=" => Ok(Self::CompatibleRelease),
33 "===" => Ok(Self::ArbitraryEqual),
34 _ => Err(()),
35 }
36 }
37}
38
39#[derive(Debug, PartialEq)]
41pub enum MarkerOp {
42 Comparison(Comparison),
43 In,
44 NotIn,
45}
46
47#[derive(Debug, PartialEq)]
49pub enum MarkerExpr {
50 Basic(String, MarkerOp, String),
51 And(Box<Self>, Box<Self>),
52 Or(Box<Self>, Box<Self>),
53}
54
55impl From<Comparison> for MarkerOp {
56 fn from(c: Comparison) -> Self {
57 Self::Comparison(c)
58 }
59}
60
61#[derive(Debug, PartialEq)]
62pub enum VersionControlSystem {
63 Git,
64 Mercurial,
65 Subversion,
66 Bazaar,
67 Unknown,
68}
69
70#[derive(Debug, PartialEq)]
72pub struct VersionSpec(pub Comparison, pub String);
73
74impl From<(Comparison, String)> for VersionSpec {
75 fn from((c, v): (Comparison, String)) -> Self {
76 Self(c, v)
77 }
78}
79
80impl VersionSpec {
81 pub fn contains(&self, version: &str) -> bool {
84 if let Ok((_, v)) = version_scheme(version) {
85 match self.0 {
86 Comparison::CompatibleRelease => self.compare_compatible(&v, &self.1),
87 Comparison::Equal => self.compare_equal(&v, &self.1),
88 Comparison::NotEqual => self.compare_not_equal(&v, &self.1),
89 Comparison::LessThanOrEqual => self.compare_less_than_equal(&v, &self.1),
90 Comparison::GreaterThanOrEqual => self.compare_greater_than_equal(&v, &self.1),
91 Comparison::LessThan => self.compare_less_than(&v, &self.1),
92 Comparison::GreaterThan => self.compare_greater_than(&v, &self.1),
93 Comparison::ArbitraryEqual => self.compare_arbitrary(&v, &self.1),
94 }
95 } else {
96 false
98 }
99 }
100
101 fn compare_compatible(&self, prospective: &Version, spec: &str) -> bool {
103 if let Ok(("", v)) = version_scheme(spec) {
104 self.compare_greater_than_equal(prospective, spec)
106 && self.compare_equal(prospective, &v.prefix_str())
107 } else {
108 false
110 }
111 }
112 fn compare_equal(&self, prospective: &Version, spec: &str) -> bool {
114 if spec.ends_with(".*") {
119 if let Ok(("", spec_v)) = version_scheme(&spec[..spec.len() - 2]) {
120 if prospective.epoch != spec_v.epoch {
121 return false;
122 }
123 for i in 0..prospective.release.len().min(spec_v.release.len()) {
126 if prospective.release[i] != spec_v.release[i] {
127 return false;
128 }
129 }
130 if spec_v.release.len() > prospective.release.len() {
133 return spec_v.release[prospective.release.len()..spec_v.release.len()]
134 .iter()
135 .all(|&i| i == 0);
136 }
137 true
138 } else {
139 false
140 }
141 } else {
142 if let Ok(("", mut spec_v)) = version_scheme(spec) {
143 if spec_v.local.is_none() && prospective.local.is_some() {
144 spec_v.local = prospective.local.clone();
145 }
146 prospective.eq(&spec_v)
147 } else {
148 false
149 }
150 }
151 }
152 fn compare_not_equal(&self, prospective: &Version, spec: &str) -> bool {
153 !self.compare_equal(prospective, spec)
154 }
155 fn compare_less_than_equal(&self, prospective: &Version, spec: &str) -> bool {
156 if let Ok(("", spec_v)) = version_scheme(spec) {
157 prospective.to_public() <= spec_v
158 } else {
159 false
160 }
161 }
162 fn compare_greater_than_equal(&self, prospective: &Version, spec: &str) -> bool {
163 if let Ok(("", spec_v)) = version_scheme(spec) {
164 prospective.to_public() >= spec_v
165 } else {
166 false
167 }
168 }
169 fn compare_less_than(&self, prospective: &Version, spec: &str) -> bool {
170 if let Ok(("", spec_v)) = version_scheme(spec) {
171 if !(prospective < &spec_v) {
172 return false;
173 }
174 if !spec_v.is_prerelease() && prospective.is_prerelease() {
175 if prospective.to_base() == spec_v.to_base() {
176 return false;
177 }
178 }
179 true
180 } else {
181 false
182 }
183 }
184 fn compare_greater_than(&self, prospective: &Version, spec: &str) -> bool {
185 if let Ok(("", spec_v)) = version_scheme(spec) {
186 if !(prospective > &spec_v) {
187 return false;
188 }
189 if !spec_v.is_postrelease() && prospective.is_postrelease() {
190 if prospective.to_base() == spec_v.to_base() {
191 return false;
192 }
193 }
194 if prospective.local.is_some() {
195 if prospective.to_base() == spec_v.to_base() {
196 return false;
197 }
198 }
199 true
200 } else {
201 false
202 }
203 }
204 fn compare_arbitrary(&self, prospective: &Version, spec: &str) -> bool {
205 prospective.to_string().eq_ignore_ascii_case(spec)
206 }
207}
208
209#[derive(Debug, PartialEq, Default)]
210pub struct RequirementSpecifier {
211 pub name: String,
212 pub extras: Vec<String>,
213 pub version_specs: Vec<VersionSpec>,
214 pub urlspec: Option<String>,
215 pub marker_expr: Option<MarkerExpr>,
216}
217
218impl RequirementSpecifier {
219 pub fn contains_version(&self, version: &str) -> bool {
220 self.version_specs.iter().all(|spec| spec.contains(version))
221 }
222}
223
224#[derive(Debug, Clone, Eq)]
225pub enum LocalVersionPart {
226 Num(u64),
227 LowerStr(String),
228}
229
230impl Ord for LocalVersionPart {
232 fn cmp(&self, other: &Self) -> Ordering {
233 match self {
234 Self::Num(self_n) => match other {
235 Self::Num(other_n) => self_n.cmp(other_n),
236 Self::LowerStr(_) => Ordering::Greater,
237 },
238 Self::LowerStr(self_s) => match other {
239 Self::Num(_) => Ordering::Less,
240 Self::LowerStr(other_s) => self_s.cmp(other_s),
241 },
242 }
243 }
244}
245
246impl PartialOrd for LocalVersionPart {
247 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
248 Some(self.cmp(other))
249 }
250}
251
252impl PartialEq for LocalVersionPart {
253 fn eq(&self, other: &Self) -> bool {
254 self.cmp(other) == Ordering::Equal
255 }
256}
257
258impl Display for LocalVersionPart {
259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
260 match self {
261 LocalVersionPart::LowerStr(s) => write!(f, "{}", s),
262 LocalVersionPart::Num(n) => write!(f, "{}", n),
263 }
264 }
265}
266
267#[derive(Debug, Default, Eq)]
271pub struct Version {
272 pub epoch: u64,
273 pub release: Vec<u64>,
274 pub pre: Option<(String, u64)>,
275 pub post: Option<(String, u64)>,
276 pub dev: Option<(String, u64)>,
277 pub local: Option<Vec<LocalVersionPart>>,
278}
279
280impl Ord for Version {
288 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
289 self.cmpkey().cmp(&other.cmpkey())
290 }
291}
292
293impl PartialOrd for Version {
294 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
295 Some(self.cmp(other))
296 }
297}
298
299impl PartialEq for Version {
300 fn eq(&self, other: &Self) -> bool {
301 self.cmp(other) == Ordering::Equal
302 }
303}
304
305static NEGATIVE_INFINITY_LOCAL: Vec<LocalVersionPart> = vec![];
306static INFINITY_PRE_POST_DEV: (&'static str, u64) = ("~", u64::MAX);
307static NEGATIVE_INFINITY_PRE_POST_DEV: (&'static str, u64) = ("!", 0);
308impl Version {
309 pub fn cmpkey(
310 &self,
311 ) -> (
312 u64,
313 Vec<u64>,
314 (&str, u64),
315 (&str, u64),
316 (&str, u64),
317 &Vec<LocalVersionPart>,
318 ) {
319 let pre = if self.pre.is_none() && self.post.is_none() && self.dev.is_some() {
320 NEGATIVE_INFINITY_PRE_POST_DEV
321 } else {
322 match self.pre {
323 None => INFINITY_PRE_POST_DEV,
324 Some((ref l, n)) => (l.as_str(), n),
325 }
326 };
327 let post = match self.post {
328 None => NEGATIVE_INFINITY_PRE_POST_DEV,
329 Some((ref l, n)) => (l.as_str(), n),
330 };
331 let dev = match self.dev {
332 None => INFINITY_PRE_POST_DEV,
333 Some((ref l, n)) => (l.as_str(), n),
334 };
335 let local = match self.local {
336 None => &NEGATIVE_INFINITY_LOCAL,
337 Some(ref v) => v,
338 };
339 (
340 self.epoch,
341 self.release_without_trailing_zero(),
342 pre,
343 post,
344 dev,
345 local,
346 )
347 }
348
349 pub fn release_without_trailing_zero(&self) -> Vec<u64> {
351 self.release
352 .iter()
353 .rev()
354 .skip_while(|&&r| r == 0)
355 .collect::<Vec<&u64>>()
356 .iter()
357 .rev()
358 .map(|&&x| x)
359 .collect()
360 }
361
362 pub fn prefix_str(&self) -> String {
364 let mut parts = String::new();
365 if self.epoch != 0 {
367 parts.push_str(&format!("{}!", self.epoch));
368 }
369 if self.release.len() > 1 {
371 for i in &self.release[..self.release.len() - 1] {
372 parts.push_str(&format!("{}.", i));
373 }
374 parts.truncate(parts.len() - 1);
375 }
376 parts.push_str(".*");
377 parts
378 }
379
380 pub fn is_prerelease(&self) -> bool {
381 self.dev.is_some() || self.pre.is_some()
382 }
383
384 pub fn is_postrelease(&self) -> bool {
385 self.post.is_some()
386 }
387
388 pub fn to_public(&self) -> Self {
391 Self {
392 epoch: self.epoch.clone(),
393 release: self.release.clone(),
394 pre: self.pre.clone(),
395 post: self.post.clone(),
396 dev: self.dev.clone(),
397 local: None,
398 }
399 }
400
401 pub fn to_base(&self) -> Self {
402 Self {
403 epoch: self.epoch.clone(),
404 release: self.release.clone(),
405 pre: None,
406 post: None,
407 dev: None,
408 local: None,
409 }
410 }
411
412 pub fn public_str(&self) -> String {
413 self.canonicalize_str(false, false)
414 }
415
416 pub fn canonicalize_str(&self, strip_trailing_zero: bool, with_local: bool) -> String {
420 let mut parts = String::new();
421 if self.epoch != 0 {
423 parts.push_str(&format!("{}!", self.epoch));
424 }
425 if strip_trailing_zero {
427 for i in self
428 .release
429 .iter()
430 .rev()
431 .skip_while(|&&r| r == 0)
432 .collect::<Vec<&u64>>()
433 .iter()
434 .rev()
435 {
436 parts.push_str(&format!("{}.", i));
437 }
438 } else {
439 for i in self.release.iter() {
440 parts.push_str(&format!("{}.", i));
441 }
442 }
443 parts.truncate(parts.len() - 1);
444 if let Some((l, n)) = self.pre.as_ref() {
446 parts.push_str(&format!("{}{}", l, n));
447 }
448 if let Some((_, n)) = self.post.as_ref() {
450 parts.push_str(&format!(".post{}", n));
451 }
452 if let Some((_, n)) = self.dev.as_ref() {
454 parts.push_str(&format!(".dev{}", n));
455 }
456 if with_local {
458 if let Some(local) = self.local.as_ref() {
459 parts.push_str("+");
460 for i in local.iter() {
461 parts.push_str(&format!("{}.", i));
462 }
463 parts.truncate(parts.len() - 1);
464 }
465 }
466 parts
467 }
468}
469
470impl Display for Version {
472 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
473 write!(f, "{}", self.canonicalize_str(false, true))
474 }
475}