1// Copyright (c) 2018 The predicates-rs Project Developers.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/license/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Definition of boolean logic combinators over `Predicate`s.
10
11use std::fmt;
12use std::marker::PhantomData;
13
14use crate::reflection;
15use crate::Predicate;
16
17/// Predicate that combines two `Predicate`s, returning the AND of the results.
18///
19/// This is created by the `Predicate::and` function.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct AndPredicate<M1, M2, Item>
22where
23 M1: Predicate<Item>,
24 M2: Predicate<Item>,
25 Item: ?Sized,
26{
27 a: M1,
28 b: M2,
29 _phantom: PhantomData<Item>,
30}
31
32unsafe impl<M1, M2, Item> Send for AndPredicate<M1, M2, Item>
33where
34 M1: Predicate<Item> + Send,
35 M2: Predicate<Item> + Send,
36 Item: ?Sized,
37{
38}
39
40unsafe impl<M1, M2, Item> Sync for AndPredicate<M1, M2, Item>
41where
42 M1: Predicate<Item> + Sync,
43 M2: Predicate<Item> + Sync,
44 Item: ?Sized,
45{
46}
47
48impl<M1, M2, Item> AndPredicate<M1, M2, Item>
49where
50 M1: Predicate<Item>,
51 M2: Predicate<Item>,
52 Item: ?Sized,
53{
54 /// Create a new `AndPredicate` over predicates `a` and `b`.
55 pub fn new(a: M1, b: M2) -> AndPredicate<M1, M2, Item> {
56 AndPredicate {
57 a,
58 b,
59 _phantom: PhantomData,
60 }
61 }
62}
63
64impl<M1, M2, Item> Predicate<Item> for AndPredicate<M1, M2, Item>
65where
66 M1: Predicate<Item>,
67 M2: Predicate<Item>,
68 Item: ?Sized,
69{
70 fn eval(&self, item: &Item) -> bool {
71 self.a.eval(item) && self.b.eval(item)
72 }
73
74 fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option<reflection::Case<'a>> {
75 let child_a = self.a.find_case(expected, variable);
76 match (expected, child_a) {
77 (true, Some(child_a)) => self.b.find_case(expected, variable).map(|child_b| {
78 reflection::Case::new(Some(self), expected)
79 .add_child(child_a)
80 .add_child(child_b)
81 }),
82 (true, None) => None,
83 (false, Some(child_a)) => {
84 Some(reflection::Case::new(Some(self), expected).add_child(child_a))
85 }
86 (false, None) => self
87 .b
88 .find_case(expected, variable)
89 .map(|child_b| reflection::Case::new(Some(self), expected).add_child(child_b)),
90 }
91 }
92}
93
94impl<M1, M2, Item> reflection::PredicateReflection for AndPredicate<M1, M2, Item>
95where
96 M1: Predicate<Item>,
97 M2: Predicate<Item>,
98 Item: ?Sized,
99{
100 fn children<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Child<'a>> + 'a> {
101 let params = vec![
102 reflection::Child::new("left", &self.a),
103 reflection::Child::new("right", &self.b),
104 ];
105 Box::new(params.into_iter())
106 }
107}
108
109impl<M1, M2, Item> fmt::Display for AndPredicate<M1, M2, Item>
110where
111 M1: Predicate<Item>,
112 M2: Predicate<Item>,
113 Item: ?Sized,
114{
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 write!(f, "({} && {})", self.a, self.b)
117 }
118}
119
120#[cfg(test)]
121mod test_and {
122 use crate::prelude::*;
123
124 #[test]
125 fn find_case_true() {
126 assert!(predicate::always()
127 .and(predicate::always())
128 .find_case(true, &5)
129 .is_some());
130 }
131
132 #[test]
133 fn find_case_true_left_fail() {
134 assert!(predicate::never()
135 .and(predicate::always())
136 .find_case(true, &5)
137 .is_none());
138 }
139
140 #[test]
141 fn find_case_true_right_fail() {
142 assert!(predicate::always()
143 .and(predicate::never())
144 .find_case(true, &5)
145 .is_none());
146 }
147
148 #[test]
149 fn find_case_true_fails() {
150 assert!(predicate::never()
151 .and(predicate::never())
152 .find_case(true, &5)
153 .is_none());
154 }
155
156 #[test]
157 fn find_case_false() {
158 assert!(predicate::never()
159 .and(predicate::never())
160 .find_case(false, &5)
161 .is_some());
162 }
163
164 #[test]
165 fn find_case_false_fails() {
166 assert!(predicate::always()
167 .and(predicate::always())
168 .find_case(false, &5)
169 .is_none());
170 }
171
172 #[test]
173 fn find_case_false_left_fail() {
174 assert!(predicate::never()
175 .and(predicate::always())
176 .find_case(false, &5)
177 .is_some());
178 }
179
180 #[test]
181 fn find_case_false_right_fail() {
182 assert!(predicate::always()
183 .and(predicate::never())
184 .find_case(false, &5)
185 .is_some());
186 }
187}
188
189/// Predicate that combines two `Predicate`s, returning the OR of the results.
190///
191/// This is created by the `Predicate::or` function.
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
193pub struct OrPredicate<M1, M2, Item>
194where
195 M1: Predicate<Item>,
196 M2: Predicate<Item>,
197 Item: ?Sized,
198{
199 a: M1,
200 b: M2,
201 _phantom: PhantomData<Item>,
202}
203
204unsafe impl<M1, M2, Item> Send for OrPredicate<M1, M2, Item>
205where
206 M1: Predicate<Item> + Send,
207 M2: Predicate<Item> + Send,
208 Item: ?Sized,
209{
210}
211
212unsafe impl<M1, M2, Item> Sync for OrPredicate<M1, M2, Item>
213where
214 M1: Predicate<Item> + Sync,
215 M2: Predicate<Item> + Sync,
216 Item: ?Sized,
217{
218}
219
220impl<M1, M2, Item> OrPredicate<M1, M2, Item>
221where
222 M1: Predicate<Item>,
223 M2: Predicate<Item>,
224 Item: ?Sized,
225{
226 /// Create a new `OrPredicate` over predicates `a` and `b`.
227 pub fn new(a: M1, b: M2) -> OrPredicate<M1, M2, Item> {
228 OrPredicate {
229 a,
230 b,
231 _phantom: PhantomData,
232 }
233 }
234}
235
236impl<M1, M2, Item> Predicate<Item> for OrPredicate<M1, M2, Item>
237where
238 M1: Predicate<Item>,
239 M2: Predicate<Item>,
240 Item: ?Sized,
241{
242 fn eval(&self, item: &Item) -> bool {
243 self.a.eval(item) || self.b.eval(item)
244 }
245
246 fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option<reflection::Case<'a>> {
247 let child_a = self.a.find_case(expected, variable);
248 match (expected, child_a) {
249 (true, Some(child_a)) => {
250 Some(reflection::Case::new(Some(self), expected).add_child(child_a))
251 }
252 (true, None) => self
253 .b
254 .find_case(expected, variable)
255 .map(|child_b| reflection::Case::new(Some(self), expected).add_child(child_b)),
256 (false, Some(child_a)) => self.b.find_case(expected, variable).map(|child_b| {
257 reflection::Case::new(Some(self), expected)
258 .add_child(child_a)
259 .add_child(child_b)
260 }),
261 (false, None) => None,
262 }
263 }
264}
265
266impl<M1, M2, Item> reflection::PredicateReflection for OrPredicate<M1, M2, Item>
267where
268 M1: Predicate<Item>,
269 M2: Predicate<Item>,
270 Item: ?Sized,
271{
272 fn children<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Child<'a>> + 'a> {
273 let params = vec![
274 reflection::Child::new("left", &self.a),
275 reflection::Child::new("right", &self.b),
276 ];
277 Box::new(params.into_iter())
278 }
279}
280
281impl<M1, M2, Item> fmt::Display for OrPredicate<M1, M2, Item>
282where
283 M1: Predicate<Item>,
284 M2: Predicate<Item>,
285 Item: ?Sized,
286{
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 write!(f, "({} || {})", self.a, self.b)
289 }
290}
291
292#[cfg(test)]
293mod test_or {
294 use crate::prelude::*;
295
296 #[test]
297 fn find_case_true() {
298 assert!(predicate::always()
299 .or(predicate::always())
300 .find_case(true, &5)
301 .is_some());
302 }
303
304 #[test]
305 fn find_case_true_left_fail() {
306 assert!(predicate::never()
307 .or(predicate::always())
308 .find_case(true, &5)
309 .is_some());
310 }
311
312 #[test]
313 fn find_case_true_right_fail() {
314 assert!(predicate::always()
315 .or(predicate::never())
316 .find_case(true, &5)
317 .is_some());
318 }
319
320 #[test]
321 fn find_case_true_fails() {
322 assert!(predicate::never()
323 .or(predicate::never())
324 .find_case(true, &5)
325 .is_none());
326 }
327
328 #[test]
329 fn find_case_false() {
330 assert!(predicate::never()
331 .or(predicate::never())
332 .find_case(false, &5)
333 .is_some());
334 }
335
336 #[test]
337 fn find_case_false_fails() {
338 assert!(predicate::always()
339 .or(predicate::always())
340 .find_case(false, &5)
341 .is_none());
342 }
343
344 #[test]
345 fn find_case_false_left_fail() {
346 assert!(predicate::never()
347 .or(predicate::always())
348 .find_case(false, &5)
349 .is_none());
350 }
351
352 #[test]
353 fn find_case_false_right_fail() {
354 assert!(predicate::always()
355 .or(predicate::never())
356 .find_case(false, &5)
357 .is_none());
358 }
359}
360
361/// Predicate that returns a `Predicate` taking the logical NOT of the result.
362///
363/// This is created by the `Predicate::not` function.
364#[derive(Debug, Clone, Copy, PartialEq, Eq)]
365pub struct NotPredicate<M, Item>
366where
367 M: Predicate<Item>,
368 Item: ?Sized,
369{
370 inner: M,
371 _phantom: PhantomData<Item>,
372}
373
374unsafe impl<M, Item> Send for NotPredicate<M, Item>
375where
376 M: Predicate<Item> + Send,
377 Item: ?Sized,
378{
379}
380
381unsafe impl<M, Item> Sync for NotPredicate<M, Item>
382where
383 M: Predicate<Item> + Sync,
384 Item: ?Sized,
385{
386}
387
388impl<M, Item> NotPredicate<M, Item>
389where
390 M: Predicate<Item>,
391 Item: ?Sized,
392{
393 /// Create a new `NotPredicate` over predicate `inner`.
394 pub fn new(inner: M) -> NotPredicate<M, Item> {
395 NotPredicate {
396 inner,
397 _phantom: PhantomData,
398 }
399 }
400}
401
402impl<M, Item> Predicate<Item> for NotPredicate<M, Item>
403where
404 M: Predicate<Item>,
405 Item: ?Sized,
406{
407 fn eval(&self, item: &Item) -> bool {
408 !self.inner.eval(item)
409 }
410
411 fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option<reflection::Case<'a>> {
412 self.inner
413 .find_case(!expected, variable)
414 .map(|child| reflection::Case::new(Some(self), expected).add_child(child))
415 }
416}
417
418impl<M, Item> reflection::PredicateReflection for NotPredicate<M, Item>
419where
420 M: Predicate<Item>,
421 Item: ?Sized,
422{
423 fn children<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Child<'a>> + 'a> {
424 let params = vec![reflection::Child::new("predicate", &self.inner)];
425 Box::new(params.into_iter())
426 }
427}
428
429impl<M, Item> fmt::Display for NotPredicate<M, Item>
430where
431 M: Predicate<Item>,
432 Item: ?Sized,
433{
434 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435 write!(f, "(! {})", self.inner)
436 }
437}
438
439/// `Predicate` extension that adds boolean logic.
440pub trait PredicateBooleanExt<Item: ?Sized>
441where
442 Self: Predicate<Item>,
443{
444 /// Compute the logical AND of two `Predicate` results, returning the result.
445 ///
446 /// # Examples
447 ///
448 /// ```
449 /// use predicates::prelude::*;
450 ///
451 /// let predicate_fn1 = predicate::always().and(predicate::always());
452 /// let predicate_fn2 = predicate::always().and(predicate::never());
453 /// assert_eq!(true, predicate_fn1.eval(&4));
454 /// assert_eq!(false, predicate_fn2.eval(&4));
455 fn and<B>(self, other: B) -> AndPredicate<Self, B, Item>
456 where
457 B: Predicate<Item>,
458 Self: Sized,
459 {
460 AndPredicate::new(self, other)
461 }
462
463 /// Compute the logical OR of two `Predicate` results, returning the result.
464 ///
465 /// # Examples
466 ///
467 /// ```
468 /// use predicates::prelude::*;
469 ///
470 /// let predicate_fn1 = predicate::always().or(predicate::always());
471 /// let predicate_fn2 = predicate::always().or(predicate::never());
472 /// let predicate_fn3 = predicate::never().or(predicate::never());
473 /// assert_eq!(true, predicate_fn1.eval(&4));
474 /// assert_eq!(true, predicate_fn2.eval(&4));
475 /// assert_eq!(false, predicate_fn3.eval(&4));
476 fn or<B>(self, other: B) -> OrPredicate<Self, B, Item>
477 where
478 B: Predicate<Item>,
479 Self: Sized,
480 {
481 OrPredicate::new(self, other)
482 }
483
484 /// Compute the logical NOT of a `Predicate`, returning the result.
485 ///
486 /// # Examples
487 ///
488 /// ```
489 /// use predicates::prelude::*;
490 ///
491 /// let predicate_fn1 = predicate::always().not();
492 /// let predicate_fn2 = predicate::never().not();
493 /// assert_eq!(false, predicate_fn1.eval(&4));
494 /// assert_eq!(true, predicate_fn2.eval(&4));
495 fn not(self) -> NotPredicate<Self, Item>
496 where
497 Self: Sized,
498 {
499 NotPredicate::new(self)
500 }
501}
502
503impl<P, Item> PredicateBooleanExt<Item> for P
504where
505 P: Predicate<Item>,
506 Item: ?Sized,
507{
508}
509