pkgcraft/restrict/
boolean.rs

1/// Create a restriction enum injected with boolean variants.
2macro_rules! restrict_with_boolean {
3   ($name:ident, $($variants:tt)*) => {
4        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
5        pub enum $name {
6            $($variants)*
7            And(Vec<Box<Self>>),
8            Or(Vec<Box<Self>>),
9            Xor(Vec<Box<Self>>),
10            Not(Box<Self>),
11        }
12   }
13}
14pub(crate) use restrict_with_boolean;
15
16/// Implement restriction matching for a type with injected boolean variants.
17macro_rules! restrict_match_boolean {
18   ($r:expr, $obj:expr, $($matcher:pat $(if $pred:expr)* => $result:expr,)+) => {
19       match $r {
20           $($matcher $(if $pred)* => $result,)+
21            Self::And(vals) => vals.iter().all(|r| r.matches($obj)),
22            Self::Or(vals) => vals.iter().any(|r| r.matches($obj)),
23            Self::Xor(vals) => {
24                let mut curr: Option<bool>;
25                let mut prev: Option<bool> = None;
26                for r in vals {
27                    curr = Some(r.matches($obj));
28                    if prev.is_some() && curr != prev {
29                        return true;
30                    }
31                    prev = curr
32                }
33                false
34            },
35            Self::Not(r) => !r.matches($obj),
36       }
37   }
38}
39pub(crate) use restrict_match_boolean;
40
41/// Implement boolean restriction conversions for injected boolean variants.
42macro_rules! restrict_impl_boolean {
43    ($type:ty) => {
44        pub fn and<I>(iter: I) -> Self
45        where
46            I: IntoIterator,
47            I::Item: Into<$type>,
48        {
49            let mut restricts = vec![];
50            for r in iter.into_iter().map(Into::into) {
51                match r {
52                    Self::And(vals) => restricts.extend(vals),
53                    _ => restricts.push(Box::new(r)),
54                }
55            }
56
57            if restricts.len() == 1 {
58                *restricts.pop().unwrap()
59            } else {
60                Self::And(restricts)
61            }
62        }
63
64        pub fn or<I>(iter: I) -> Self
65        where
66            I: IntoIterator,
67            I::Item: Into<$type>,
68        {
69            let mut restricts = vec![];
70            for r in iter.into_iter().map(Into::into) {
71                match r {
72                    Self::Or(vals) => restricts.extend(vals),
73                    _ => restricts.push(Box::new(r)),
74                }
75            }
76
77            if restricts.len() == 1 {
78                *restricts.pop().unwrap()
79            } else {
80                Self::Or(restricts)
81            }
82        }
83
84        pub fn xor<I>(iter: I) -> Self
85        where
86            I: IntoIterator,
87            I::Item: Into<$type>,
88        {
89            let mut restricts = vec![];
90            for r in iter.into_iter().map(Into::into) {
91                match r {
92                    Self::Xor(vals) => restricts.extend(vals),
93                    _ => restricts.push(Box::new(r)),
94                }
95            }
96
97            if restricts.len() == 1 {
98                *restricts.pop().unwrap()
99            } else {
100                Self::Xor(restricts)
101            }
102        }
103
104        pub fn not<T>(obj: T) -> Self
105        where
106            T: Into<$type>,
107        {
108            Self::Not(Box::new(obj.into()))
109        }
110    };
111}
112pub(crate) use restrict_impl_boolean;
113
114macro_rules! restrict_ops_boolean {
115    ($type:ty) => {
116        impl std::ops::BitAnd for $type {
117            type Output = Self;
118
119            fn bitand(self, rhs: Self) -> Self::Output {
120                Self::and([self, rhs])
121            }
122        }
123
124        impl std::ops::BitOr for $type {
125            type Output = Self;
126
127            fn bitor(self, rhs: Self) -> Self::Output {
128                Self::or([self, rhs])
129            }
130        }
131
132        impl std::ops::BitXor for $type {
133            type Output = Self;
134
135            fn bitxor(self, rhs: Self) -> Self::Output {
136                Self::xor([self, rhs])
137            }
138        }
139
140        impl std::ops::Not for $type {
141            type Output = Self;
142
143            fn not(self) -> Self::Output {
144                Self::Not(Box::new(self))
145            }
146        }
147    };
148}
149pub(crate) use restrict_ops_boolean;
150
151#[cfg(test)]
152mod tests {
153    use crate::dep::Dep;
154    use crate::restrict::dep::Restrict as DepRestrict;
155    use crate::restrict::{Restrict as BaseRestrict, Restriction};
156
157    #[test]
158    fn and() {
159        let d = Dep::try_new("cat/pkg").unwrap();
160        let cat = DepRestrict::category("cat");
161        let pkg = DepRestrict::package("pkg");
162        let r = BaseRestrict::and([cat, pkg]);
163        assert!(r.matches(&d));
164
165        // one matched and one unmatched restriction
166        let cat = DepRestrict::category("cat");
167        let pkg = DepRestrict::package("pkga");
168        let r = BaseRestrict::and([cat, pkg]);
169        assert!(!(r.matches(&d)));
170
171        // matching against two deps
172        let d1 = Dep::try_new("cat/pkg1").unwrap();
173        let d2 = Dep::try_new("cat/pkg2").unwrap();
174        let r = BaseRestrict::and([&d1, &d2]);
175        assert!(!(r.matches(&d1)));
176        assert!(!(r.matches(&d2)));
177    }
178
179    #[test]
180    fn or() {
181        let d = Dep::try_new("cat/pkg").unwrap();
182        let cat = DepRestrict::category("cat");
183        let pkg = DepRestrict::package("pkg");
184        let r = BaseRestrict::or([cat, pkg]);
185        assert!(r.matches(&d));
186
187        // one matched and one unmatched restriction
188        let cat = DepRestrict::category("cat");
189        let pkg = DepRestrict::package("pkga");
190        let r = BaseRestrict::or([cat, pkg]);
191        assert!(r.matches(&d));
192
193        // matching against two deps
194        let d1 = Dep::try_new("cat/pkg1").unwrap();
195        let d2 = Dep::try_new("cat/pkg2").unwrap();
196        let r = BaseRestrict::or([&d1, &d2]);
197        assert!(r.matches(&d1));
198        assert!(r.matches(&d2));
199    }
200
201    #[test]
202    fn xor() {
203        let d = Dep::try_new("cat/pkg").unwrap();
204
205        let cat = DepRestrict::category("cat");
206        let pkg = DepRestrict::package("pkg");
207        let nover = DepRestrict::Version(None);
208
209        // two matches
210        let r = BaseRestrict::xor([cat.clone(), pkg.clone()]);
211        assert!(!(r.matches(&d)));
212
213        // three matches
214        let r = BaseRestrict::xor([cat, pkg, nover.clone()]);
215        assert!(!(r.matches(&d)));
216
217        let cat = DepRestrict::category("cat");
218        let pkg = DepRestrict::package("pkga");
219        let ver = DepRestrict::version("1").unwrap();
220
221        // one matched and one unmatched
222        let r = BaseRestrict::xor([cat.clone(), pkg.clone()]);
223        assert!(r.matches(&d));
224
225        // one matched and two unmatched
226        let r = BaseRestrict::xor([cat.clone(), pkg.clone(), ver]);
227        assert!(r.matches(&d));
228
229        // two matched and one unmatched
230        let r = BaseRestrict::xor([cat, pkg, nover]);
231        assert!(r.matches(&d));
232
233        let d1 = Dep::try_new("cat/pkg1").unwrap();
234        let d2 = Dep::try_new("cat/pkg2").unwrap();
235        let d3 = Dep::try_new("cat/pkg3").unwrap();
236
237        // two non-matches
238        let r = BaseRestrict::xor([&d1, &d2]);
239        assert!(!(r.matches(&d)));
240
241        // three non-matches
242        let r = BaseRestrict::xor([&d1, &d2, &d3]);
243        assert!(!(r.matches(&d)));
244    }
245
246    #[test]
247    fn not() {
248        let d = Dep::try_new("cat/pkg").unwrap();
249        let r: BaseRestrict = DepRestrict::category("cat1").into();
250
251        // restrict doesn't match
252        assert!(!(r.matches(&d)));
253
254        // inverse matches
255        assert!(!r.matches(&d));
256    }
257}