Skip to main content

vivacity_resolver/
platform_filter.rs

1//! Port of `Composer\Filter\PlatformRequirementFilter\*`: what
2//! `--ignore-platform-reqs` / `--ignore-platform-req` remove from the rules.
3
4use 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
19/// `BasePackage::packageNamesToRegexp` (`{^(?:a|b)$}iD`).
20fn 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    /// `PlatformRequirementFilterFactory::fromBoolOrList`.
33    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    /// `IgnoreListPlatformRequirementFilter::filterConstraint`; identity for
72    /// the other two filters (which do not have this method).
73    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}