1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::fmt;
4use std::hash::{Hash, Hasher};
5
6#[cfg(feature = "python")]
7pub mod python;
8
9mod sortkey;
10pub use sortkey::*;
11
12#[derive(Clone, Debug, Default, Eq, PartialEq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33pub struct Nevra<'a> {
34 name: Cow<'a, str>,
35 evr: Evr<'a>,
36 arch: Cow<'a, str>,
37}
38
39impl<'a> Nevra<'a> {
40 pub fn new<T: Into<Cow<'a, str>>>(
42 name: T,
43 epoch: T,
44 version: T,
45 release: T,
46 arch: T,
47 ) -> Nevra<'a> {
48 Self {
49 name: name.into(),
50 evr: Evr::new(epoch, version, release),
51 arch: arch.into(),
52 }
53 }
54
55 pub fn parse(nevra: &'a str) -> Self {
57 let (n, e, v, r, a) = Nevra::parse_values(nevra);
58 Self::new(n, e, v, r, a)
59 }
60
61 pub fn name(&self) -> &str {
63 &self.name
64 }
65
66 pub fn evr(&'a self) -> &'a Evr<'a> {
68 &self.evr
69 }
70
71 pub fn epoch(&self) -> &str {
73 &self.evr.epoch
74 }
75
76 pub fn version(&self) -> &str {
78 &self.evr.version
79 }
80
81 pub fn release(&self) -> &str {
83 &self.evr.release
84 }
85
86 pub fn arch(&self) -> &str {
88 &self.arch
89 }
90
91 pub fn values(&self) -> (&str, &str, &str, &str, &str) {
93 (
94 &self.name,
95 &self.evr.epoch,
96 &self.evr.version,
97 &self.evr.release,
98 &self.arch,
99 )
100 }
101
102 pub fn parse_values(nevra: &'a str) -> (&'a str, &'a str, &'a str, &'a str, &'a str) {
104 let (nevr, arch) = nevra.rsplit_once('.').unwrap_or((nevra, ""));
107
108 let (nev, release) = nevr.rsplit_once('-').unwrap_or((nevr, ""));
111
112 let (name, version_epoch) = nev.rsplit_once('-').unwrap_or((nev, ""));
115
116 let (epoch, version) = match version_epoch.split_once(':') {
119 Some((e, v)) => (e, v),
121 None => ("", version_epoch),
123 };
124
125 (name, epoch, version, release, arch)
126 }
127
128 pub fn nevra(&self) -> String {
135 format!("{}-{}.{}", self.name, self.evr.evr(), self.arch)
136 }
137
138 pub fn nevra_short(&self) -> String {
145 self.to_string()
146 }
147
148 pub fn nvra(&self) -> String {
154 format!(
155 "{}-{}-{}.{}",
156 self.name, self.evr.version, self.evr.release, self.arch
157 )
158 }
159}
160
161impl Hash for Nevra<'_> {
162 fn hash<H: Hasher>(&self, state: &mut H) {
163 self.name.hash(state);
164 self.evr.hash(state);
165 self.arch.hash(state);
166 }
167}
168
169impl fmt::Display for Nevra<'_> {
170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 write!(f, "{}-{}.{}", self.name, self.evr, self.arch)
172 }
173}
174
175impl PartialOrd for Nevra<'_> {
176 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
177 Some(self.cmp(other))
178 }
179}
180
181impl Ord for Nevra<'_> {
182 fn cmp(&self, other: &Self) -> Ordering {
183 let name_cmp = compare_version_string(&self.name, &other.name);
184 if name_cmp != Ordering::Equal {
185 return name_cmp;
186 }
187
188 let evr_cmp = self.evr.cmp(&other.evr);
189 if evr_cmp != Ordering::Equal {
190 return evr_cmp;
191 }
192
193 compare_version_string(&self.arch, &other.arch)
194 }
195}
196
197#[derive(Clone, Debug, Default, Eq)]
218#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
219pub struct Evr<'a> {
220 epoch: Cow<'a, str>,
221 version: Cow<'a, str>,
222 release: Cow<'a, str>,
223}
224
225impl<'a> Evr<'a> {
226 pub fn new<T: Into<Cow<'a, str>>>(epoch: T, version: T, release: T) -> Evr<'a> {
228 Evr {
229 epoch: epoch.into(),
230 version: version.into(),
231 release: release.into(),
232 }
233 }
234
235 pub fn parse(evr: &'a str) -> Self {
237 Evr::parse_values(evr).into()
238 }
239
240 pub fn epoch(&self) -> &str {
242 &self.epoch
243 }
244
245 pub fn version(&self) -> &str {
247 &self.version
248 }
249
250 pub fn release(&self) -> &str {
252 &self.release
253 }
254
255 pub fn set_epoch(&mut self, epoch: impl Into<Cow<'a, str>>) {
257 self.epoch = epoch.into();
258 }
259
260 pub fn set_version(&mut self, version: impl Into<Cow<'a, str>>) {
262 self.version = version.into();
263 }
264
265 pub fn set_release(&mut self, release: impl Into<Cow<'a, str>>) {
267 self.release = release.into();
268 }
269
270 pub fn evr(&self) -> String {
276 let epoch = if self.epoch.is_empty() {
277 "0"
278 } else {
279 self.epoch.as_ref()
280 };
281
282 format!("{}:{}-{}", epoch, self.version(), self.release())
283 }
284
285 pub fn evr_short(&self) -> String {
291 self.to_string()
292 }
293
294 pub fn values(&self) -> (&str, &str, &str) {
296 (self.epoch(), self.version(), self.release())
297 }
298
299 pub fn parse_values(evr: &'a str) -> (&'a str, &'a str, &'a str) {
301 let (epoch, vr) = evr.split_once(':').unwrap_or(("", evr));
302 let (version, release) = vr.split_once('-').unwrap_or((vr, ""));
303
304 (epoch, version, release)
305 }
306
307 pub fn sortkey(&self) -> EvrSortKey {
309 EvrSortKey::from_values(&self.epoch, &self.version, &self.release)
310 }
311}
312
313impl<'a> From<(&'a str, &'a str, &'a str)> for Evr<'a> {
314 fn from(val: (&'a str, &'a str, &'a str)) -> Self {
315 Evr::new(val.0, val.1, val.2)
316 }
317}
318
319impl PartialEq for Evr<'_> {
320 #[allow(clippy::comparison_to_empty)]
321 fn eq(&self, other: &Self) -> bool {
322 ((self.epoch == other.epoch)
323 || (self.epoch == "" && other.epoch == "0")
324 || (self.epoch == "0" && other.epoch == ""))
325 && self.version == other.version
326 && self.release == other.release
327 }
328}
329
330impl Hash for Evr<'_> {
331 fn hash<H: Hasher>(&self, state: &mut H) {
332 let epoch = if self.epoch.is_empty() {
333 "0"
334 } else {
335 &self.epoch
336 };
337 epoch.hash(state);
338 self.version.hash(state);
339 self.release.hash(state);
340 }
341}
342
343impl fmt::Display for Evr<'_> {
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 if !self.epoch.is_empty() {
346 write!(f, "{}:", self.epoch)?;
347 }
348
349 write!(f, "{}-{}", self.version, self.release)
350 }
351}
352
353impl PartialOrd for Evr<'_> {
354 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
355 Some(self.cmp(other))
356 }
357}
358
359impl Ord for Evr<'_> {
360 fn cmp(&self, other: &Self) -> Ordering {
361 let epoch_1 = if self.epoch.is_empty() {
362 "0"
363 } else {
364 &self.epoch
365 };
366 let epoch_2 = if other.epoch.is_empty() {
367 "0"
368 } else {
369 &other.epoch
370 };
371
372 let epoch_cmp = compare_version_string(epoch_1, epoch_2);
373 if epoch_cmp != Ordering::Equal {
374 return epoch_cmp;
375 }
376
377 let version_cmp = compare_version_string(&self.version, &other.version);
378 if version_cmp != Ordering::Equal {
379 return version_cmp;
380 }
381
382 compare_version_string(&self.release, &other.release)
383 }
384}
385
386fn compare_version_string(version1: &str, version2: &str) -> Ordering {
388 if version1 == version2 {
389 return Ordering::Equal;
390 }
391
392 let mut version1_part = version1;
393 let mut version2_part = version2;
394
395 let not_alphanumeric_tilde_or_caret =
396 |c: char| !c.is_ascii_alphanumeric() && c != '~' && c != '^';
397
398 loop {
399 version1_part = version1_part.trim_start_matches(not_alphanumeric_tilde_or_caret);
401 version2_part = version2_part.trim_start_matches(not_alphanumeric_tilde_or_caret);
402
403 match (
405 version1_part.strip_prefix('~'),
406 version2_part.strip_prefix('~'),
407 ) {
408 (Some(_), None) => return Ordering::Less,
409 (None, Some(_)) => return Ordering::Greater,
410 (Some(a), Some(b)) => {
411 version1_part = a;
412 version2_part = b;
413 continue;
414 }
415 _ => (),
416 }
417
418 match (
421 version1_part.strip_prefix('^'),
422 version2_part.strip_prefix('^'),
423 ) {
424 (Some(_), None) => match version2_part.is_empty() {
425 true => return Ordering::Greater,
426 false => return Ordering::Less,
427 },
428 (None, Some(_)) => match version1_part.is_empty() {
429 true => return Ordering::Less,
430 false => return Ordering::Greater,
431 },
432 (Some(a), Some(b)) => {
433 version1_part = a;
434 version2_part = b;
435 continue;
436 }
437 _ => (),
438 }
439
440 if version1_part.is_empty() || version2_part.is_empty() {
441 break;
442 }
443
444 fn matching_contiguous<F>(string: &str, pat: F) -> Option<(&str, &str)>
447 where
448 F: Fn(char) -> bool,
449 {
450 let end = string.find(|c| !pat(c)).unwrap_or(string.len());
451 if end == 0 {
452 None
453 } else {
454 Some(string.split_at(end))
455 }
456 }
457
458 if version1_part.starts_with(|c: char| c.is_ascii_digit()) {
459 match (
460 matching_contiguous(version1_part, |c| c.is_ascii_digit()),
461 matching_contiguous(version2_part, |c| c.is_ascii_digit()),
462 ) {
463 (Some((prefix1, rest1)), Some((prefix2, rest2))) => {
464 version1_part = rest1;
465 version2_part = rest2;
466
467 let prefix1 = prefix1.trim_start_matches('0');
468 let prefix2 = prefix2.trim_start_matches('0');
469
470 let ordering = prefix1.len().cmp(&prefix2.len());
471 if ordering != Ordering::Equal {
472 return ordering;
473 }
474 let ordering = prefix1.cmp(prefix2);
475 if ordering != Ordering::Equal {
476 return ordering;
477 }
478 }
479 (Some(_), None) => return Ordering::Greater,
480 _ => unreachable!(),
481 }
482 } else {
483 match (
484 matching_contiguous(version1_part, |c| c.is_ascii_alphabetic()),
485 matching_contiguous(version2_part, |c| c.is_ascii_alphabetic()),
486 ) {
487 (Some((prefix1, rest1)), Some((prefix2, rest2))) => {
488 version1_part = rest1;
489 version2_part = rest2;
490
491 let ordering = prefix1.cmp(prefix2);
492 if ordering != Ordering::Equal {
493 return ordering;
494 }
495 }
496 (Some(_), None) => return Ordering::Less,
497 _ => unreachable!(),
498 }
499 }
500 }
501
502 version1_part.len().cmp(&version2_part.len())
503}
504
505pub fn rpm_evr_compare(evr1: &str, evr2: &str) -> Ordering {
507 let evr1 = Evr::parse(evr1);
508 let evr2 = Evr::parse(evr2);
509 evr1.cmp(&evr2)
510}
511
512#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
514#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
515pub enum ReqOperator {
516 LT,
517 LE,
518 EQ,
519 GE,
520 GT,
521}
522
523impl fmt::Display for ReqOperator {
524 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
525 f.write_str(match self {
526 ReqOperator::LT => "<",
527 ReqOperator::LE => "<=",
528 ReqOperator::EQ => "=",
529 ReqOperator::GE => ">=",
530 ReqOperator::GT => ">",
531 })
532 }
533}
534
535#[derive(Clone, Debug, PartialEq, Eq, Hash)]
540#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
541pub struct Requirement<'a> {
542 name: Cow<'a, str>,
543 constraint: Option<(ReqOperator, Evr<'a>)>,
544}
545
546impl<'a> Requirement<'a> {
547 pub fn new<T: Into<Cow<'a, str>>>(name: T) -> Self {
549 Self {
550 name: name.into(),
551 constraint: None,
552 }
553 }
554
555 pub fn with_constraint<T: Into<Cow<'a, str>>>(name: T, op: ReqOperator, evr: Evr<'a>) -> Self {
557 Self {
558 name: name.into(),
559 constraint: Some((op, evr)),
560 }
561 }
562
563 pub fn name(&self) -> &str {
565 &self.name
566 }
567
568 pub fn constraint(&self) -> Option<(ReqOperator, &Evr<'a>)> {
570 self.constraint.as_ref().map(|(op, evr)| (*op, evr))
571 }
572
573 pub fn satisfies(&self, name: &str, evr: &Evr) -> bool {
575 if self.name != name {
576 return false;
577 }
578 match &self.constraint {
579 None => true,
580 Some((op, req_evr)) => {
581 let ord = evr.cmp(req_evr);
582 match op {
583 ReqOperator::LT => ord == Ordering::Less,
584 ReqOperator::LE => ord != Ordering::Greater,
585 ReqOperator::EQ => ord == Ordering::Equal,
586 ReqOperator::GE => ord != Ordering::Less,
587 ReqOperator::GT => ord == Ordering::Greater,
588 }
589 }
590 }
591 }
592}
593
594impl fmt::Display for Requirement<'_> {
595 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
596 match &self.constraint {
597 None => write!(f, "{}", self.name),
598 Some((op, evr)) => write!(f, "{} {} {}", self.name, op, evr),
599 }
600 }
601}
602
603#[cfg(test)]
604mod test {
605 use super::*;
606
607 fn assert_ver_order(a: &str, b: &str, expected: Ordering) {
610 assert_eq!(
611 compare_version_string(a, b),
612 expected,
613 "compare_version_string({a:?}, {b:?})"
614 );
615 assert_eq!(
616 version_sortkey(a).cmp(&version_sortkey(b)),
617 expected,
618 "version_sortkey({a:?}) vs version_sortkey({b:?})"
619 );
620 }
621
622 fn assert_evr_order(a: Evr, b: Evr, expected: Ordering) {
625 assert_eq!(a.cmp(&b), expected, "Evr::cmp({a:?}, {b:?})");
626 assert_eq!(
627 a.sortkey().cmp(&b.sortkey()),
628 expected,
629 "Evr::sortkey({a:?}) vs Evr::sortkey({b:?})"
630 );
631 }
632
633 #[test]
635 fn test_nevra_tostr() {
636 let nevra = Nevra::new("foo", "", "1.2.3", "45", "x86_64");
637 assert_eq!("foo-1.2.3-45.x86_64", nevra.to_string());
638 assert_eq!("foo-1.2.3-45.x86_64", nevra.nevra_short());
639 assert_eq!("foo-0:1.2.3-45.x86_64", nevra.nevra());
640
641 let nevra = Nevra::new("foo", "0", "1.2.3", "45", "x86_64");
642 assert_eq!("foo-0:1.2.3-45.x86_64", nevra.to_string());
643 assert_eq!("foo-0:1.2.3-45.x86_64", nevra.nevra());
644
645 let nevra = Nevra::new("foo", "1", "2.3.4", "5", "x86_64");
646 assert_eq!("foo-1:2.3.4-5.x86_64", nevra.to_string());
647 assert_eq!("foo-1:2.3.4-5.x86_64", nevra.nevra());
648
649 let nevra = Nevra::new("python3.9", "0", "3.9.11", "2.fc38", "x86_64");
650 assert_eq!("python3.9-0:3.9.11-2.fc38.x86_64", nevra.to_string());
651 assert_eq!("python3.9-0:3.9.11-2.fc38.x86_64", nevra.nevra());
652 }
653
654 #[test]
656 fn test_nevra_parse() {
657 let nevra = Nevra::new("foo", "", "1.2.3", "45", "x86_64");
658 assert_eq!(Nevra::parse("foo-1.2.3-45.x86_64"), nevra);
659
660 let nevra = Nevra::new("foo", "0", "1.2.3", "45", "x86_64");
661 assert_eq!(Nevra::parse("foo-0:1.2.3-45.x86_64"), nevra);
662
663 let nevra = Nevra::new("foo", "1", "2.3.4", "5", "x86_64");
664 assert_eq!(Nevra::parse("foo-1:2.3.4-5.x86_64"), nevra);
665
666 let nevra = Nevra::new("python3.9", "0", "3.9.11", "2", "x86_64");
667 assert_eq!(Nevra::parse("python3.9-3.9.11-2.x86_64"), nevra);
668
669 let nevra = Nevra::new("python3.9", "0", "3.9.11", "2.fc38", "x86_64");
670 assert_eq!(Nevra::parse("python3.9-3.9.11-2.fc38.x86_64"), nevra);
671 }
672
673 #[test]
675 fn test_nevra_parse_edge_cases() {
676 assert_eq!(Nevra::parse_values("foo"), ("foo", "", "", "", ""));
677 assert_eq!(
678 Nevra::parse_values("foo-1.2-3.bar"),
679 ("foo", "", "1.2", "3", "bar")
680 );
681 assert_eq!(
682 Nevra::parse_values("foo-1.2-3.bar.x86_64"),
683 ("foo", "", "1.2", "3.bar", "x86_64")
684 );
685 assert_eq!(
686 Nevra::parse_values("python3.9-3.9.11-2.fc38.x86_64"),
687 ("python3.9", "", "3.9.11", "2.fc38", "x86_64")
688 );
689
690 let nevra = Nevra::new("python3.9-devel", "0", "3.9.11", "2.fc38", "x86_64");
691 assert_eq!(Nevra::parse("python3.9-devel-3.9.11-2.fc38.x86_64"), nevra);
692
693 let nevra = Nevra::new("foo-bar", "", "1.2.3", "45", "x86_64");
694 assert_eq!(Nevra::parse("foo-bar-1.2.3-45.x86_64"), nevra);
695
696 let nevra = Nevra::new("foo-bar", "0", "1.2.3", "45", "x86_64");
697 assert_eq!(Nevra::parse("foo-bar-0:1.2.3-45.x86_64"), nevra);
698
699 let nevra = Nevra::new("foo-bar-0", "", "1.2.3", "45.el10", "x86_64");
700 assert_eq!(Nevra::parse("foo-bar-0-1.2.3-45.el10.x86_64"), nevra);
701
702 let nevra = Nevra::new("foo-bar-0", "0", "1.2.3", "45.el10", "x86_64");
703 assert_eq!(Nevra::parse("foo-bar-0-0:1.2.3-45.el10.x86_64"), nevra);
704
705 let nevra = Nevra::new("grub2-efi-x64", "1", "2.12", "28.fc42", "x86_64");
706 assert_eq!(Nevra::parse("grub2-efi-x64-1:2.12-28.fc42.x86_64"), nevra);
707 }
708
709 #[test]
711 fn test_nevra_ord() {
712 let nevra1 = Nevra::parse("foo-1.2.3-45.noarch");
713 let nevra2 = Nevra::parse("foo-1.2.3-45.noarch");
714 assert!(nevra1 == nevra2);
715
716 let nevra1 = Nevra::parse("foo-1.2.3-45.noarch");
717 let nevra2 = Nevra::parse("foo-0:1.2.3-45.noarch");
718 assert!(nevra1 == nevra2);
719
720 let nevra1 = Nevra::parse("bar-1.2.3-45.noarch");
721 let nevra2 = Nevra::parse("foo-9:1.2.3-45.noarch");
722 assert!(nevra1 < nevra2);
723
724 let nevra1 = Nevra::parse("foo-1.2.3-45.noarch");
725 let nevra2 = Nevra::parse("foobar-1.2.3-45.noarch");
726 assert!(nevra1 < nevra2);
727
728 let nevra1 = Nevra::parse("foo-2.3.4-5.noarch");
729 let nevra2 = Nevra::parse("foobar-1.2.3-45.noarch");
730 assert!(nevra1 < nevra2);
731
732 let nevra1 = Nevra::parse("bar-1.2.3-45.noarch");
733 let nevra2 = Nevra::parse("foo-1.2.3-45.noarch");
734 assert!(nevra1 < nevra2);
735
736 let nevra1 = Nevra::parse("foo-1.2.3-45.fc38.noarch");
737 let nevra2 = Nevra::parse("foo-1.2.3-45.fc39.noarch");
738 assert!(nevra1 < nevra2);
739
740 let nevra1 = Nevra::parse("foo-1.2.3-45.fc39.i386");
741 let nevra2 = Nevra::parse("foo-1.2.3-45.fc39.x86_64");
742 assert!(nevra1 < nevra2);
743
744 let nevra1 = Nevra::parse("python3.9-3.9.12-2.fc39.i386");
745 let nevra2 = Nevra::parse("python3.11-3.11.7-2.fc39.x86_64");
746 assert!(nevra1 < nevra2);
747
748 let nevra1 = Nevra::parse("python3.11-3.11.7-2.fc39.x86_64");
749 let nevra2 = Nevra::parse("python3.9-3.9.12-2.fc39.x86_64");
750 assert!(nevra1 > nevra2);
751 }
752
753 #[test]
755 fn test_evr_tostr() {
756 let evr = Evr::new("", "1.2.3", "45");
757 assert_eq!("1.2.3-45", evr.to_string());
758 assert_eq!("1.2.3-45", evr.evr_short());
759 assert_eq!("0:1.2.3-45", evr.evr());
760
761 let evr = Evr::new("0", "1.2.3", "45");
762 assert_eq!("0:1.2.3-45", evr.to_string());
763 assert_eq!("0:1.2.3-45", evr.evr());
764 }
765
766 #[test]
768 fn test_evr_parse() {
769 let evr = Evr::new("", "1.2.3", "45");
770 assert_eq!(Evr::parse("1.2.3-45"), evr);
771
772 let evr = Evr::new("0", "1.2.3", "45");
773 assert_eq!(Evr::parse("0:1.2.3-45"), evr);
774
775 let evr = Evr::new("1", "2.3.4", "5");
776 assert_eq!(Evr::parse("1:2.3.4-5"), evr);
777 }
778
779 #[test]
781 fn test_evr_parse_edge_cases() {
782 assert_eq!(Evr::parse_values("-"), ("", "", ""));
783 assert_eq!(Evr::parse_values("."), ("", ".", ""));
784 assert_eq!(Evr::parse_values(":"), ("", "", ""));
785 assert_eq!(Evr::parse_values(":-"), ("", "", ""));
786 assert_eq!(Evr::parse_values(".-"), ("", ".", ""));
787 assert_eq!(Evr::parse_values("0"), ("", "0", ""));
788 assert_eq!(Evr::parse_values("0-"), ("", "0", ""));
789 assert_eq!(Evr::parse_values(":0"), ("", "0", ""));
790 assert_eq!(Evr::parse_values(":0-"), ("", "0", ""));
791 assert_eq!(Evr::parse_values("0:"), ("0", "", ""));
792 assert_eq!(Evr::parse_values("asdf:"), ("asdf", "", ""));
793 assert_eq!(Evr::parse_values("~:"), ("~", "", ""));
794 }
795
796 #[test]
798 fn test_rpm_evr_compare() {
799 assert_eq!(Ordering::Equal, rpm_evr_compare("0:1.2.3-45", "1.2.3-45"));
800 assert_eq!(Ordering::Less, rpm_evr_compare("1.2.3-45", "1:1.2.3-45"));
801 assert_eq!(Ordering::Greater, rpm_evr_compare("1.2.3-46", "1.2.3-45"));
802 }
803
804 #[test]
806 fn test_evr_ord() {
807 assert_evr_order(
809 Evr::parse("1.2.3-45"),
810 Evr::parse("1.2.3-45"),
811 Ordering::Equal,
812 );
813 assert_evr_order(
814 Evr::parse("2:1.2.3-45"),
815 Evr::parse("2:1.2.3-45"),
816 Ordering::Equal,
817 );
818 assert_evr_order(
820 Evr::parse("1.2.3-45"),
821 Evr::parse("0:1.2.3-45"),
822 Ordering::Equal,
823 );
824 assert_evr_order(
826 Evr::parse("1.2.3-45"),
827 Evr::parse("1:1.2.3-45"),
828 Ordering::Less,
829 );
830 assert_evr_order(
832 Evr::parse("4.2.3-45"),
833 Evr::parse("1:1.2.3-45"),
834 Ordering::Less,
835 );
836
837 assert_evr_order(
839 Evr::parse("1.2.3-45"),
840 Evr::parse("1.2.4-45"),
841 Ordering::Less,
842 );
843 assert_evr_order(
844 Evr::parse("1.23.3-45"),
845 Evr::parse("1.2.3-45"),
846 Ordering::Greater,
847 );
848 assert_evr_order(
849 Evr::parse("12.2.3-45"),
850 Evr::parse("1.2.3-45"),
851 Ordering::Greater,
852 );
853 assert_evr_order(
854 Evr::parse("1.2.3-45"),
855 Evr::parse("1.12.3-45"),
856 Ordering::Less,
857 );
858
859 assert_evr_order(
861 Evr::parse("~1.2.3-45"),
862 Evr::parse("1.2.3-45"),
863 Ordering::Less,
864 );
865 assert_evr_order(
866 Evr::parse("~12.2.3-45"),
867 Evr::parse("1.2.3-45"),
868 Ordering::Less,
869 );
870 assert_evr_order(
871 Evr::parse("~12.2.3-45"),
872 Evr::parse("~1.2.3-45"),
873 Ordering::Greater,
874 );
875 assert_evr_order(
877 Evr::parse("3:~1.2.3-45"),
878 Evr::parse("0:1.2.3-45"),
879 Ordering::Greater,
880 );
881
882 assert_evr_order(
884 Evr::parse("1.2.3-45"),
885 Evr::parse("1.2.3-46"),
886 Ordering::Less,
887 );
888 assert_evr_order(
889 Evr::parse("1.2.3-45.fc39"),
890 Evr::parse("1.2.3-46.fc38"),
891 Ordering::Less,
892 );
893 assert_evr_order(
894 Evr::parse("1.2.3-3"),
895 Evr::parse("1.2.3-10"),
896 Ordering::Less,
897 );
898 assert_evr_order(
899 Evr::parse("1.2.3-3.fc40"),
900 Evr::parse("1.2.3-10.fc39"),
901 Ordering::Less,
902 );
903 }
904
905 #[test]
907 fn test_compare_version_string() {
908 assert_ver_order("1.0", "1.0", Ordering::Equal);
909 assert_ver_order("1.0", "2.0", Ordering::Less);
910 assert_ver_order("2.0", "1.0", Ordering::Greater);
911
912 assert_ver_order("2.0.1", "2.0.1", Ordering::Equal);
913 assert_ver_order("2.0", "2.0.1", Ordering::Less);
914 assert_ver_order("2.0.1", "2.0", Ordering::Greater);
915
916 assert_ver_order("5.0.1", "5.0.1a", Ordering::Less);
917 assert_ver_order("5.0.1a", "5.0.1", Ordering::Greater);
918
919 assert_ver_order("5.0.a1", "5.0.a1", Ordering::Equal);
920 assert_ver_order("5.0.1a", "5.0.1a", Ordering::Equal);
921 assert_ver_order("5.0.a1", "5.0.a2", Ordering::Less);
922 assert_ver_order("5.0.a2", "5.0.a1", Ordering::Greater);
923
924 assert_ver_order("10abc", "10.1abc", Ordering::Less);
925 assert_ver_order("10.1abc", "10abc", Ordering::Greater);
926
927 assert_ver_order("8.0", "8.0.rc1", Ordering::Less);
928 assert_ver_order("8.0.rc1", "8.0", Ordering::Greater);
929
930 assert_ver_order("10b2", "10a1", Ordering::Greater);
931 assert_ver_order("10a2", "10b2", Ordering::Less);
932
933 assert_ver_order("6.6p1", "7.5p1", Ordering::Less);
934 assert_ver_order("7.5p1", "6.6p1", Ordering::Greater);
935
936 assert_ver_order("6.5p1", "6.5p1", Ordering::Equal);
937 assert_ver_order("6.5p1", "6.5p2", Ordering::Less);
938 assert_ver_order("6.5p2", "6.5p1", Ordering::Greater);
939 assert_ver_order("6.5p2", "6.6p1", Ordering::Less);
940 assert_ver_order("6.6p1", "6.5p2", Ordering::Greater);
941
942 assert_ver_order("6.5p10", "6.5p10", Ordering::Equal);
943 assert_ver_order("6.5p1", "6.5p10", Ordering::Less);
944 assert_ver_order("6.5p10", "6.5p1", Ordering::Greater);
945
946 assert_ver_order("abc10", "abc10", Ordering::Equal);
947 assert_ver_order("abc10", "abc10.1", Ordering::Less);
948 assert_ver_order("abc10.1", "abc10", Ordering::Greater);
949
950 assert_ver_order("abc.4", "abc.4", Ordering::Equal);
951 assert_ver_order("abc.4", "8", Ordering::Less);
952 assert_ver_order("8", "abc.4", Ordering::Greater);
953 assert_ver_order("abc.4", "2", Ordering::Less);
954 assert_ver_order("2", "abc.4", Ordering::Greater);
955
956 assert_ver_order("1.0aa", "1.0aa", Ordering::Equal);
957 assert_ver_order("1.0a", "1.0aa", Ordering::Less);
958 assert_ver_order("1.0aa", "1.0a", Ordering::Greater);
959 }
960
961 #[test]
963 fn test_version_comparison_numeric_handling() {
964 assert_ver_order("10.0001", "10.0001", Ordering::Equal);
965 assert_ver_order("10.0001", "10.1", Ordering::Equal);
967 assert_ver_order("10.1", "10.0001", Ordering::Equal);
968 assert_ver_order("10.0001", "10.0039", Ordering::Less);
969 assert_ver_order("10.0039", "10.0001", Ordering::Greater);
970 assert_ver_order("10.1", "10.10001", Ordering::Less);
972 assert_ver_order("10.1111", "10.10001", Ordering::Less);
973 assert_ver_order("10.11111", "10.10001", Ordering::Greater);
974
975 assert_ver_order("20240521", "20240521", Ordering::Equal);
976 assert_ver_order("20240521", "20240522", Ordering::Less);
977 assert_ver_order("20240522", "20240521", Ordering::Greater);
978 assert_ver_order("20240521", "202405210", Ordering::Less);
979 }
980
981 #[test]
983 fn test_version_comparison_tilde_and_caret() {
984 assert_ver_order("1.0~rc1", "1.0~rc1", Ordering::Equal);
985 assert_ver_order("1.0~rc1", "1.0", Ordering::Less);
986 assert_ver_order("1.0", "1.0~rc1", Ordering::Greater);
987 assert_ver_order("1.0~rc1", "1.0~rc2", Ordering::Less);
988 assert_ver_order("1.0~rc2", "1.0~rc1", Ordering::Greater);
989 assert_ver_order("1.0~rc1~git123", "1.0~rc1~git123", Ordering::Equal);
990 assert_ver_order("1.0~rc1~git123", "1.0~rc1", Ordering::Less);
991 assert_ver_order("1.0~rc1", "1.0~rc1~git123", Ordering::Greater);
992
993 assert_ver_order("1.0^", "1.0^", Ordering::Equal);
994 assert_ver_order("1.0", "1.0^", Ordering::Less);
995 assert_ver_order("1.0^", "1.0", Ordering::Greater);
996
997 assert_ver_order("1.0", "1.0git1^", Ordering::Less);
998 assert_ver_order("1.0^git1", "1.0^git2", Ordering::Less);
999 assert_ver_order("1.01", "1.0^git1", Ordering::Greater);
1000 assert_ver_order("1.0^20240501", "1.0^20240501", Ordering::Equal);
1001 assert_ver_order("1.0^20240501", "1.0.1", Ordering::Less);
1002 assert_ver_order("1.0^20240501^git1", "1.0^20240501^git1", Ordering::Equal);
1003 assert_ver_order("1.0^20240502", "1.0^20240501^git1", Ordering::Greater);
1004 assert_ver_order("1.0~rc1^git1", "1.0~rc1^git1", Ordering::Equal);
1005 assert_ver_order("1.0~rc1", "1.0~rc1^git1", Ordering::Less);
1006 assert_ver_order("1.0~rc1^git1", "1.0~rc1", Ordering::Greater);
1007 assert_ver_order("1.0^git1~pre", "1.0^git1~pre", Ordering::Equal);
1008 assert_ver_order("1.0^git1~pre", "1.0^git1", Ordering::Less);
1009 assert_ver_order("1.0^git1", "1.0^git1~pre", Ordering::Greater);
1010 }
1011
1012 #[test]
1015 fn test_non_intuitive_comparison_behavior() {
1016 assert_ver_order("1e.fc33", "1.fc33", Ordering::Less);
1017 assert_ver_order("1g.fc33", "1.fc33", Ordering::Greater);
1018 }
1019
1020 #[test]
1022 fn test_non_alphanumeric_equivalence() {
1023 assert_ver_order("b", "b", Ordering::Equal);
1025 assert_ver_order("b+", "b+", Ordering::Equal);
1026 assert_ver_order("b+", "b_", Ordering::Equal);
1027 assert_ver_order("b_", "b+", Ordering::Equal);
1028 assert_ver_order("+b", "+b", Ordering::Equal);
1029 assert_ver_order("+b", "_b", Ordering::Equal);
1030 assert_ver_order("_b", "+b", Ordering::Equal);
1031
1032 assert_ver_order("+b", "++b", Ordering::Equal);
1033 assert_ver_order("+b", "+b+", Ordering::Equal);
1034
1035 assert_ver_order("+.", "+_", Ordering::Equal);
1036 assert_ver_order("_+", "+.", Ordering::Equal);
1037 assert_ver_order("+", ".", Ordering::Equal);
1038 assert_ver_order(",", "+", Ordering::Equal);
1039
1040 assert_ver_order("++", "_", Ordering::Equal);
1041 assert_ver_order("+", "..", Ordering::Equal);
1042
1043 assert_ver_order("4_0", "4_0", Ordering::Equal);
1044 assert_ver_order("4_0", "4.0", Ordering::Equal);
1045 assert_ver_order("4.0", "4_0", Ordering::Equal);
1046
1047 assert_ver_order("4.999", "5.0", Ordering::Less);
1048 assert_ver_order("4.999.9", "5.0", Ordering::Less);
1049 assert_ver_order("5.0", "4.999_9", Ordering::Greater);
1050
1051 assert_ver_order("4.999", "4.999.9", Ordering::Less);
1053 assert_ver_order("4.999", "4.99.9", Ordering::Greater);
1054 }
1055
1056 #[test]
1058 fn test_non_ascii_character_equivalence() {
1059 assert_ver_order("1.1.Á.1", "1.1.1", Ordering::Equal);
1061 assert_ver_order("1.1.Á", "1.1.Á", Ordering::Equal);
1062 assert_ver_order("1.1.Á", "1.1.Ê", Ordering::Equal);
1063 assert_ver_order("1.1.ÁÁ", "1.1.Á", Ordering::Equal);
1064 assert_ver_order("1.1.Á", "1.1.ÊÊ", Ordering::Equal);
1065
1066 assert_ver_order("1.1Á1", "1.11", Ordering::Less);
1068 }
1069
1070 #[test]
1072 fn test_evr_hash_consistency() {
1073 use std::hash::{DefaultHasher, Hash, Hasher};
1074
1075 fn hash_of<T: Hash>(val: &T) -> u64 {
1076 let mut h = DefaultHasher::new();
1077 val.hash(&mut h);
1078 h.finish()
1079 }
1080
1081 let evr1 = Evr::parse("1.2.3-45");
1083 let evr2 = Evr::parse("0:1.2.3-45");
1084 assert_eq!(evr1, evr2);
1085 assert_eq!(hash_of(&evr1), hash_of(&evr2));
1086
1087 let evr3 = Evr::parse("2:1.2.3-45");
1089 let evr4 = Evr::parse("2:1.2.3-45");
1090 assert_eq!(hash_of(&evr3), hash_of(&evr4));
1091
1092 assert_ne!(hash_of(&evr1), hash_of(&evr3));
1094 }
1095
1096 #[test]
1098 fn test_nevra_hash_consistency() {
1099 use std::hash::{DefaultHasher, Hash, Hasher};
1100
1101 fn hash_of<T: Hash>(val: &T) -> u64 {
1102 let mut h = DefaultHasher::new();
1103 val.hash(&mut h);
1104 h.finish()
1105 }
1106
1107 let nevra1 = Nevra::parse("foo-1.2.3-45.noarch");
1108 let nevra2 = Nevra::parse("foo-0:1.2.3-45.noarch");
1109 assert_eq!(nevra1, nevra2);
1110 assert_eq!(hash_of(&nevra1), hash_of(&nevra2));
1111
1112 let nevra3 = Nevra::parse("foo-1:1.2.3-45.noarch");
1113 assert_ne!(hash_of(&nevra1), hash_of(&nevra3));
1114 }
1115
1116 #[cfg(feature = "serde")]
1117 #[test]
1118 fn test_evr_serde_roundtrip() {
1119 let evr = Evr::parse("1:2.3.4-5");
1120 let json = serde_json::to_string(&evr).unwrap();
1121 let evr2: Evr = serde_json::from_str(&json).unwrap();
1122 assert_eq!(evr, evr2);
1123 }
1124
1125 #[cfg(feature = "serde")]
1126 #[test]
1127 fn test_nevra_serde_roundtrip() {
1128 let nevra = Nevra::parse("foo-1:2.3.4-5.x86_64");
1129 let json = serde_json::to_string(&nevra).unwrap();
1130 let nevra2: Nevra = serde_json::from_str(&json).unwrap();
1131 assert_eq!(nevra, nevra2);
1132 }
1133
1134 #[test]
1135 fn test_requirement_no_constraint() {
1136 let req = Requirement::new("foo");
1137 assert!(req.satisfies("foo", &Evr::parse("1.0-1")));
1138 assert!(req.satisfies("foo", &Evr::parse("999.0-1")));
1139 assert!(!req.satisfies("bar", &Evr::parse("1.0-1")));
1140 }
1141
1142 #[test]
1143 fn test_requirement_eq() {
1144 let req = Requirement::with_constraint("foo", ReqOperator::EQ, Evr::parse("1.0-1"));
1145 assert!(req.satisfies("foo", &Evr::parse("1.0-1")));
1146 assert!(req.satisfies("foo", &Evr::parse("0:1.0-1")));
1147 assert!(!req.satisfies("foo", &Evr::parse("1.0-2")));
1148 assert!(!req.satisfies("foo", &Evr::parse("2.0-1")));
1149 }
1150
1151 #[test]
1152 fn test_requirement_ge() {
1153 let req = Requirement::with_constraint("foo", ReqOperator::GE, Evr::parse("1.0-1"));
1154 assert!(req.satisfies("foo", &Evr::parse("1.0-1")));
1155 assert!(req.satisfies("foo", &Evr::parse("2.0-1")));
1156 assert!(!req.satisfies("foo", &Evr::parse("0.9-1")));
1157 }
1158
1159 #[test]
1160 fn test_requirement_lt() {
1161 let req = Requirement::with_constraint("foo", ReqOperator::LT, Evr::parse("2.0-1"));
1162 assert!(req.satisfies("foo", &Evr::parse("1.0-1")));
1163 assert!(!req.satisfies("foo", &Evr::parse("2.0-1")));
1164 assert!(!req.satisfies("foo", &Evr::parse("3.0-1")));
1165 }
1166
1167 #[test]
1168 fn test_requirement_display() {
1169 let req = Requirement::new("foo");
1170 assert_eq!(req.to_string(), "foo");
1171
1172 let req = Requirement::with_constraint("foo", ReqOperator::GE, Evr::parse("1:2.0-1"));
1173 assert_eq!(req.to_string(), "foo >= 1:2.0-1");
1174 }
1175}