predicates/
boolean.rs

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}