pkgcraft/restrict/
boolean.rs1macro_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
16macro_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
41macro_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 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 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 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 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 let r = BaseRestrict::xor([cat.clone(), pkg.clone()]);
211 assert!(!(r.matches(&d)));
212
213 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 let r = BaseRestrict::xor([cat.clone(), pkg.clone()]);
223 assert!(r.matches(&d));
224
225 let r = BaseRestrict::xor([cat.clone(), pkg.clone(), ver]);
227 assert!(r.matches(&d));
228
229 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 let r = BaseRestrict::xor([&d1, &d2]);
239 assert!(!(r.matches(&d)));
240
241 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 assert!(!(r.matches(&d)));
253
254 assert!(!r.matches(&d));
256 }
257}