vivacity_resolver/
platform_filter.rs1use crate::constraint::{Constraint, Op};
5use crate::intervals;
6use crate::platform::is_platform_package;
7use crate::version::preg_quote;
8use pcre2::bytes::Regex;
9
10pub enum PlatformRequirementFilter {
11 IgnoreNothing,
12 IgnoreAll,
13 IgnoreList {
14 ignore: Regex,
15 ignore_upper_bound: Regex,
16 },
17}
18
19fn package_names_regexp(names: &[String]) -> Regex {
21 let parts: Vec<String> = names
22 .iter()
23 .map(|n| preg_quote(n).replace("\\*", ".*"))
24 .collect();
25 pcre2::bytes::RegexBuilder::new()
26 .caseless(true)
27 .build(&format!("^(?:{})\\z", parts.join("|")))
28 .unwrap_or_else(|e| panic!("package names regexp: {e}"))
29}
30
31impl PlatformRequirementFilter {
32 pub fn from_list(reqs: &[String]) -> PlatformRequirementFilter {
34 let mut ignore_all = Vec::new();
35 let mut ignore_upper = Vec::new();
36 for req in reqs {
37 match req.strip_suffix('+') {
38 Some(base) => ignore_upper.push(base.to_owned()),
39 None => ignore_all.push(req.clone()),
40 }
41 }
42 PlatformRequirementFilter::IgnoreList {
43 ignore: package_names_regexp(&ignore_all),
44 ignore_upper_bound: package_names_regexp(&ignore_upper),
45 }
46 }
47
48 pub fn is_ignored(&self, req: &str) -> bool {
49 match self {
50 PlatformRequirementFilter::IgnoreNothing => false,
51 PlatformRequirementFilter::IgnoreAll => is_platform_package(req),
52 PlatformRequirementFilter::IgnoreList { ignore, .. } => {
53 is_platform_package(req) && ignore.is_match(req.as_bytes()).unwrap_or(false)
54 }
55 }
56 }
57
58 pub fn is_upper_bound_ignored(&self, req: &str) -> bool {
59 match self {
60 PlatformRequirementFilter::IgnoreList {
61 ignore_upper_bound, ..
62 } => {
63 is_platform_package(req)
64 && (self.is_ignored(req)
65 || ignore_upper_bound.is_match(req.as_bytes()).unwrap_or(false))
66 }
67 _ => self.is_ignored(req),
68 }
69 }
70
71 pub fn filter_constraint(
74 &self,
75 req: &str,
76 constraint: &Constraint,
77 allow_upper_bound_override: bool,
78 ) -> Constraint {
79 let PlatformRequirementFilter::IgnoreList {
80 ignore,
81 ignore_upper_bound,
82 } = self
83 else {
84 return constraint.clone();
85 };
86 if !is_platform_package(req) {
87 return constraint.clone();
88 }
89 if !allow_upper_bound_override
90 || !ignore_upper_bound.is_match(req.as_bytes()).unwrap_or(false)
91 {
92 return constraint.clone();
93 }
94 if ignore.is_match(req.as_bytes()).unwrap_or(false) {
95 return Constraint::MatchAll;
96 }
97 let ivs = intervals::generate(constraint, false);
98 if let Some(last) = ivs.numeric.last() {
99 if last.end.to_string() != intervals::until_positive_infinity().to_string() {
100 let end_version = match &last.end {
101 Constraint::Single { version, .. } => version.clone(),
102 _ => String::new(),
103 };
104 return Constraint::Multi {
105 constraints: vec![constraint.clone(), Constraint::new(Op::Ge, end_version)],
106 conjunctive: false,
107 };
108 }
109 }
110 constraint.clone()
111 }
112}