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