1use {
11 crate::{
12 control::ControlParagraph,
13 error::{DebianError, Result},
14 package_version::PackageVersion,
15 },
16 once_cell::sync::Lazy,
17 regex::Regex,
18 std::{
19 cmp::Ordering,
20 fmt::{Display, Formatter},
21 ops::{Deref, DerefMut},
22 str::FromStr,
23 },
24};
25
26pub static RE_DEPENDENCY: Lazy<Regex> = Lazy::new(|| {
28 Regex::new(
30 r"(?x)
31 # Package name is alphanumeric, terminating at whitespace, [ or (
32 (?P<package>[^\s\[(]+)
33 # Any number of optional spaces.
34 \s*
35 # Relationships are within an optional parenthesis.
36 (?:\(
37 # Optional spaces after (
38 \s*
39 # The relationship operator.
40 (?P<relop>(<<|<=|=|>=|>>))
41 # Optional spaces after the operator.
42 \s*
43 # Version string is everything up to space or closing parenthesis.
44 (?P<version>[^\s)]+)
45 # Trailing space before ).
46 \s*
47 \))?
48 # Any amount of space after optional relationship definition.
49 \s*
50 # Architecture restrictions are within an optional [..] field.
51 (?:\[
52 # Optional whitespace after [
53 \s*
54 # Optional negation operator.
55 (?P<arch_negate>!)?
56 \s*
57 # The architecture. May have spaces to delimit multiple values.
58 (?P<arch>[^\]]+)
59 \])?
60 ",
61 )
62 .unwrap()
63});
64
65#[derive(Clone, Copy, Debug, PartialEq)]
66pub enum VersionRelationship {
67 StrictlyEarlier,
68 EarlierOrEqual,
69 ExactlyEqual,
70 LaterOrEqual,
71 StrictlyLater,
72}
73
74impl Display for VersionRelationship {
75 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
76 match self {
77 Self::StrictlyEarlier => write!(f, "<<"),
78 Self::EarlierOrEqual => write!(f, "<="),
79 Self::ExactlyEqual => write!(f, "="),
80 Self::LaterOrEqual => write!(f, ">="),
81 Self::StrictlyLater => write!(f, ">>"),
82 }
83 }
84}
85
86#[derive(Clone, Debug, PartialEq)]
88pub struct DependencyVersionConstraint {
89 pub relationship: VersionRelationship,
90 pub version: PackageVersion,
91}
92
93#[derive(Clone, Debug, PartialEq)]
95pub struct SingleDependency {
96 pub package: String,
98 pub version_constraint: Option<DependencyVersionConstraint>,
99 pub architectures: Option<(bool, Vec<String>)>,
100}
101
102impl Display for SingleDependency {
103 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
104 write!(f, "{}", self.package)?;
105 if let Some(constraint) = &self.version_constraint {
106 write!(f, " ({} {})", constraint.relationship, constraint.version)?;
107 }
108 if let Some((negate, arch)) = &self.architectures {
109 write!(f, " [{}{}]", if *negate { "!" } else { "" }, arch.join(" "))?;
110 }
111
112 Ok(())
113 }
114}
115
116impl SingleDependency {
117 pub fn parse(s: &str) -> Result<Self> {
119 let caps = RE_DEPENDENCY
120 .captures(s)
121 .ok_or_else(|| DebianError::DependencyParse(s.to_string()))?;
122
123 let package = caps["package"].to_string();
124 let dependency = match (caps.name("relop"), caps.name("version")) {
125 (Some(relop), Some(version)) => {
126 let relationship = match relop.as_str() {
127 "<<" => VersionRelationship::StrictlyEarlier,
128 "<=" => VersionRelationship::EarlierOrEqual,
129 "=" => VersionRelationship::ExactlyEqual,
130 ">=" => VersionRelationship::LaterOrEqual,
131 ">>" => VersionRelationship::StrictlyLater,
132 v => panic!("unexpected version relationship: {}", v),
133 };
134
135 let version = PackageVersion::parse(version.as_str())?;
136
137 Some(DependencyVersionConstraint {
138 relationship,
139 version,
140 })
141 }
142 _ => None,
143 };
144
145 let architectures = match (caps.name("arch_negate"), caps.name("arch")) {
146 (Some(_), Some(arch)) => Some((
147 true,
148 arch.as_str()
149 .split_ascii_whitespace()
150 .map(|x| x.to_string())
151 .collect::<Vec<_>>(),
152 )),
153 (None, Some(arch)) => Some((
154 false,
155 arch.as_str()
156 .split_ascii_whitespace()
157 .map(|x| x.to_string())
158 .collect::<Vec<_>>(),
159 )),
160 _ => None,
161 };
162
163 Ok(Self {
164 package,
165 version_constraint: dependency,
166 architectures,
167 })
168 }
169
170 pub fn package_satisfies(
174 &self,
175 package: &str,
176 version: &PackageVersion,
177 architecture: &str,
178 ) -> bool {
179 if self.package == package {
180 if let Some((negate, arches)) = &self.architectures {
181 let contains = arches.iter().any(|x| x == architecture);
182
183 if (*negate && contains) || (!*negate && !contains) {
185 return false;
186 }
187 }
188
189 if let Some(constaint) = &self.version_constraint {
191 matches!(
192 (version.cmp(&constaint.version), constaint.relationship),
193 (
194 Ordering::Equal,
195 VersionRelationship::ExactlyEqual
196 | VersionRelationship::LaterOrEqual
197 | VersionRelationship::EarlierOrEqual,
198 ) | (
199 Ordering::Less,
200 VersionRelationship::StrictlyEarlier | VersionRelationship::EarlierOrEqual,
201 ) | (
202 Ordering::Greater,
203 VersionRelationship::StrictlyLater | VersionRelationship::LaterOrEqual,
204 )
205 )
206 } else {
207 true
209 }
210 } else {
211 false
212 }
213 }
214
215 pub fn package_satisfies_virtual(
220 &self,
221 package: &str,
222 provides: Option<&DependencyVersionConstraint>,
223 ) -> bool {
224 if self.package == package {
225 if let (Some(wanted_constraint), Some(provides)) =
229 (&self.version_constraint.as_ref(), provides)
230 {
231 matches!(
232 (
233 provides.version.cmp(&wanted_constraint.version),
234 wanted_constraint.relationship,
235 provides.relationship,
236 ),
237 (
240 Ordering::Equal,
241 VersionRelationship::ExactlyEqual
242 | VersionRelationship::LaterOrEqual
243 | VersionRelationship::EarlierOrEqual,
244 VersionRelationship::ExactlyEqual
245 | VersionRelationship::LaterOrEqual
246 | VersionRelationship::EarlierOrEqual,
247 )
248 |
249 (
251 Ordering::Less,
252 VersionRelationship::EarlierOrEqual | VersionRelationship::StrictlyEarlier,
253 VersionRelationship::EarlierOrEqual | VersionRelationship::StrictlyEarlier,
254 ) |
255 (
256 Ordering::Greater,
257 VersionRelationship::LaterOrEqual | VersionRelationship::StrictlyLater,
258 VersionRelationship::LaterOrEqual | VersionRelationship::StrictlyLater,
259 )
260 )
261 } else {
262 true
263 }
264 } else {
265 false
266 }
267 }
268}
269
270#[derive(Clone, Debug, Default, PartialEq)]
271pub struct DependencyVariants(Vec<SingleDependency>);
272
273impl Display for DependencyVariants {
274 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
275 write!(
276 f,
277 "{}",
278 self.0
279 .iter()
280 .map(|x| format!("{}", x))
281 .collect::<Vec<_>>()
282 .join(" | ")
283 )
284 }
285}
286
287impl Deref for DependencyVariants {
288 type Target = Vec<SingleDependency>;
289
290 fn deref(&self) -> &Self::Target {
291 &self.0
292 }
293}
294
295impl DerefMut for DependencyVariants {
296 fn deref_mut(&mut self) -> &mut Self::Target {
297 &mut self.0
298 }
299}
300
301impl DependencyVariants {
302 pub fn package_satisfies(&self, package: &str, version: &PackageVersion, arch: &str) -> bool {
307 self.0
308 .iter()
309 .any(|variant| variant.package_satisfies(package, version, arch))
310 }
311}
312
313#[derive(Clone, Debug, PartialEq)]
315pub struct DependencyList {
316 dependencies: Vec<DependencyVariants>,
317}
318
319impl Display for DependencyList {
320 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
321 write!(
322 f,
323 "{}",
324 self.dependencies
325 .iter()
326 .map(|x| format!("{}", x))
327 .collect::<Vec<_>>()
328 .join(", ")
329 )
330 }
331}
332
333impl DependencyList {
334 pub fn parse(s: &str) -> Result<Self> {
340 let mut els = vec![];
341
342 for el in s.split(',') {
343 let el = el.trim();
345
346 let mut variants = DependencyVariants::default();
348
349 for alt in el.split('|') {
350 let alt = alt.trim();
351
352 variants.push(SingleDependency::parse(alt)?);
353 }
354
355 els.push(variants);
356 }
357
358 Ok(Self { dependencies: els })
359 }
360
361 pub fn package_satisfies(&self, package: &str, version: &PackageVersion, arch: &str) -> bool {
363 self.dependencies
364 .iter()
365 .any(|variants| variants.package_satisfies(package, version, arch))
366 }
367
368 pub fn requirements(&self) -> impl Iterator<Item = &DependencyVariants> {
373 self.dependencies.iter()
374 }
375}
376
377#[derive(Clone, Copy, Debug)]
382pub enum BinaryDependency {
383 Depends,
384 Recommends,
385 Suggests,
386 Enhances,
387 PreDepends,
388}
389
390impl FromStr for BinaryDependency {
391 type Err = DebianError;
392
393 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
394 match s {
395 "Depends" => Ok(Self::Depends),
396 "Recommends" => Ok(Self::Recommends),
397 "Suggests" => Ok(Self::Suggests),
398 "Enhances" => Ok(Self::Enhances),
399 "Pre-Depends" => Ok(Self::PreDepends),
400 _ => Err(Self::Err::UnknownBinaryDependencyField(s.to_string())),
401 }
402 }
403}
404
405impl Display for BinaryDependency {
406 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
407 write!(
408 f,
409 "{}",
410 match self {
411 Self::Depends => "Depends",
412 Self::Recommends => "Recommends",
413 Self::Suggests => "Suggests",
414 Self::Enhances => "Enhances",
415 Self::PreDepends => "Pre-Depends",
416 }
417 )
418 }
419}
420
421impl BinaryDependency {
422 pub fn values() -> &'static [Self] {
424 &[
425 Self::Depends,
426 Self::Recommends,
427 Self::Suggests,
428 Self::Enhances,
429 Self::PreDepends,
430 ]
431 }
432}
433
434#[derive(Clone, Debug)]
442pub struct PackageDependencyFields {
443 pub depends: Option<DependencyList>,
445
446 pub recommends: Option<DependencyList>,
448
449 pub suggests: Option<DependencyList>,
451
452 pub enhances: Option<DependencyList>,
454
455 pub pre_depends: Option<DependencyList>,
457
458 pub breaks: Option<DependencyList>,
460
461 pub conflicts: Option<DependencyList>,
463
464 pub provides: Option<DependencyList>,
466
467 pub replaces: Option<DependencyList>,
469
470 pub build_depends: Option<DependencyList>,
472
473 pub build_depends_indep: Option<DependencyList>,
475
476 pub build_depends_arch: Option<DependencyList>,
478
479 pub build_conflicts: Option<DependencyList>,
481
482 pub build_conflicts_indep: Option<DependencyList>,
484
485 pub build_conflicts_arch: Option<DependencyList>,
487
488 pub built_using: Option<DependencyList>,
490}
491
492impl PackageDependencyFields {
493 pub fn from_paragraph(para: &ControlParagraph) -> Result<Self> {
495 let get_field = |field| -> Result<Option<DependencyList>> {
496 if let Some(value) = para.field_str(field) {
497 Ok(Some(DependencyList::parse(value)?))
498 } else {
499 Ok(None)
500 }
501 };
502
503 Ok(Self {
504 depends: get_field("Depends")?,
505 recommends: get_field("Recommends")?,
506 suggests: get_field("Suggests")?,
507 enhances: get_field("Enhances")?,
508 pre_depends: get_field("Pre-Depends")?,
509 breaks: get_field("Breaks")?,
510 conflicts: get_field("Conflicts")?,
511 provides: get_field("Provides")?,
512 replaces: get_field("Replaces")?,
513 build_depends: get_field("Build-Depends")?,
514 build_depends_indep: get_field("Build-Depends-Indep")?,
515 build_depends_arch: get_field("Build-Depends-Arch")?,
516 build_conflicts: get_field("Build-Conflicts")?,
517 build_conflicts_indep: get_field("Build-Conflicts-Indep")?,
518 build_conflicts_arch: get_field("Build-Conflicts-Arch")?,
519 built_using: get_field("Built-Using")?,
520 })
521 }
522
523 pub fn binary_dependency(&self, field: BinaryDependency) -> Option<&DependencyList> {
525 match field {
526 BinaryDependency::Depends => self.depends.as_ref(),
527 BinaryDependency::Recommends => self.recommends.as_ref(),
528 BinaryDependency::Suggests => self.suggests.as_ref(),
529 BinaryDependency::Enhances => self.enhances.as_ref(),
530 BinaryDependency::PreDepends => self.pre_depends.as_ref(),
531 }
532 }
533}
534
535#[cfg(test)]
536mod test {
537 use super::*;
538
539 #[test]
540 fn parse_depends() -> Result<()> {
541 let dl = DependencyList::parse("libc6 (>= 2.4), libx11-6")?;
542 assert_eq!(dl.dependencies.len(), 2);
543 assert_eq!(dl.dependencies[0].0.len(), 1);
544 assert_eq!(dl.dependencies[1].0.len(), 1);
545
546 assert_eq!(
547 dl.dependencies[0].0[0],
548 SingleDependency {
549 package: "libc6".into(),
550 version_constraint: Some(DependencyVersionConstraint {
551 relationship: VersionRelationship::LaterOrEqual,
552 version: PackageVersion::parse("2.4").unwrap()
553 }),
554 architectures: None,
555 }
556 );
557 assert_eq!(
558 dl.dependencies[1].0[0],
559 SingleDependency {
560 package: "libx11-6".into(),
561 version_constraint: None,
562 architectures: None,
563 }
564 );
565
566 let dl = DependencyList::parse("libc [amd64]")?;
567 assert_eq!(dl.dependencies.len(), 1);
568 assert_eq!(dl.dependencies[0].0.len(), 1);
569 assert_eq!(
570 dl.dependencies[0].0[0],
571 SingleDependency {
572 package: "libc".into(),
573 version_constraint: None,
574 architectures: Some((false, vec!["amd64".into()])),
575 }
576 );
577
578 let dl = DependencyList::parse("libc [!amd64 i386]")?;
579 assert_eq!(dl.dependencies.len(), 1);
580 assert_eq!(dl.dependencies[0].0.len(), 1);
581 assert_eq!(
582 dl.dependencies[0].0[0],
583 SingleDependency {
584 package: "libc".into(),
585 version_constraint: None,
586 architectures: Some((true, vec!["amd64".into(), "i386".into()])),
587 }
588 );
589
590 Ok(())
591 }
592
593 #[test]
594 fn satisfies_version_constraints() -> Result<()> {
595 let dl = DependencyList::parse("libc (= 2.4)")?;
596 assert!(dl.dependencies[0].package_satisfies(
597 "libc",
598 &PackageVersion::parse("2.4")?,
599 "ignored"
600 ));
601 assert!(!dl.dependencies[0].package_satisfies(
602 "libc",
603 &PackageVersion::parse("2.3")?,
604 "ignored"
605 ));
606 assert!(!dl.dependencies[0].package_satisfies(
607 "libc",
608 &PackageVersion::parse("2.5")?,
609 "ignored"
610 ));
611 assert!(!dl.dependencies[0].package_satisfies(
612 "other",
613 &PackageVersion::parse("2.4")?,
614 "ignored"
615 ));
616
617 let dl = DependencyList::parse("libc (<= 2.4)")?;
618 assert!(dl.dependencies[0].package_satisfies(
619 "libc",
620 &PackageVersion::parse("2.3")?,
621 "ignored"
622 ));
623 assert!(dl.dependencies[0].package_satisfies(
624 "libc",
625 &PackageVersion::parse("2.4")?,
626 "ignored"
627 ));
628 assert!(!dl.dependencies[0].package_satisfies(
629 "libc",
630 &PackageVersion::parse("2.5")?,
631 "ignored"
632 ));
633 assert!(!dl.dependencies[0].package_satisfies(
634 "other",
635 &PackageVersion::parse("2.4")?,
636 "ignored"
637 ));
638
639 let dl = DependencyList::parse("libc (>= 2.4)")?;
640 assert!(!dl.dependencies[0].package_satisfies(
641 "libc",
642 &PackageVersion::parse("2.3")?,
643 "ignored"
644 ));
645 assert!(dl.dependencies[0].package_satisfies(
646 "libc",
647 &PackageVersion::parse("2.4")?,
648 "ignored"
649 ));
650 assert!(dl.dependencies[0].package_satisfies(
651 "libc",
652 &PackageVersion::parse("2.5")?,
653 "ignored"
654 ));
655 assert!(!dl.dependencies[0].package_satisfies(
656 "other",
657 &PackageVersion::parse("2.4")?,
658 "ignored"
659 ));
660
661 let dl = DependencyList::parse("libc (<< 2.4)")?;
662 assert!(dl.dependencies[0].package_satisfies(
663 "libc",
664 &PackageVersion::parse("2.3")?,
665 "ignored"
666 ));
667 assert!(!dl.dependencies[0].package_satisfies(
668 "libc",
669 &PackageVersion::parse("2.4")?,
670 "ignored"
671 ));
672 assert!(!dl.dependencies[0].package_satisfies(
673 "libc",
674 &PackageVersion::parse("2.5")?,
675 "ignored"
676 ));
677 assert!(!dl.dependencies[0].package_satisfies(
678 "other",
679 &PackageVersion::parse("2.3")?,
680 "ignored"
681 ));
682
683 let dl = DependencyList::parse("libc (>> 2.4)")?;
684 assert!(!dl.dependencies[0].package_satisfies(
685 "libc",
686 &PackageVersion::parse("2.3")?,
687 "ignored"
688 ));
689 assert!(!dl.dependencies[0].package_satisfies(
690 "libc",
691 &PackageVersion::parse("2.4")?,
692 "ignored"
693 ));
694 assert!(dl.dependencies[0].package_satisfies(
695 "libc",
696 &PackageVersion::parse("2.5")?,
697 "ignored"
698 ));
699 assert!(!dl.dependencies[0].package_satisfies(
700 "other",
701 &PackageVersion::parse("2.5")?,
702 "ignored"
703 ));
704
705 Ok(())
706 }
707
708 #[test]
709 fn satisfies_architecture_constraints() -> Result<()> {
710 let dl = DependencyList::parse("libc [amd64]")?;
711 assert!(dl.dependencies[0].package_satisfies(
712 "libc",
713 &PackageVersion::parse("2.4")?,
714 "amd64"
715 ));
716 assert!(!dl.dependencies[0].package_satisfies(
717 "libc",
718 &PackageVersion::parse("2.3")?,
719 "x86"
720 ));
721
722 let dl = DependencyList::parse("libc [amd64 i386]")?;
723 assert!(dl.dependencies[0].package_satisfies(
724 "libc",
725 &PackageVersion::parse("2.4")?,
726 "amd64"
727 ));
728 assert!(dl.dependencies[0].package_satisfies(
729 "libc",
730 &PackageVersion::parse("2.3")?,
731 "i386"
732 ));
733 assert!(!dl.dependencies[0].package_satisfies(
734 "libc",
735 &PackageVersion::parse("2.3")?,
736 "arm64"
737 ));
738
739 let dl = DependencyList::parse("libc [!amd64]")?;
740 assert!(!dl.dependencies[0].package_satisfies(
741 "libc",
742 &PackageVersion::parse("2.4")?,
743 "amd64"
744 ));
745 assert!(dl.dependencies[0].package_satisfies(
746 "libc",
747 &PackageVersion::parse("2.3")?,
748 "x86"
749 ));
750
751 let dl = DependencyList::parse("libc [!amd64 i386]")?;
752 assert!(!dl.dependencies[0].package_satisfies(
753 "libc",
754 &PackageVersion::parse("2.4")?,
755 "amd64"
756 ));
757 assert!(!dl.dependencies[0].package_satisfies(
758 "libc",
759 &PackageVersion::parse("2.3")?,
760 "i386"
761 ));
762 assert!(dl.dependencies[0].package_satisfies(
763 "libc",
764 &PackageVersion::parse("2.3")?,
765 "arm64"
766 ));
767
768 Ok(())
769 }
770}