tuplez/
predicate.rs

1//! Provides the ability to test tuples.
2
3use crate::{foreach::Mapper, Tuple, TupleLike, Unit};
4
5/// Define a unary predicate that accepts immutable reference of a value and produces a `bool` result.
6///
7/// Call [`any()`](TupleLike::any()) and [`all()`](TupleLike::all()) on a tuple requires a unary predicate
8/// that implements [`UnaryPredicate`].
9///
10/// Note that the predicate always receives an immutable reference to the element of the tuple.
11///
12/// # Quickly build a unary predicate by macros
13///
14/// Here are two ways you can quickly build a folder.
15///
16/// ## Test tuples by element types
17///
18/// The [`unary_pred!`](crate::unary_pred!) macro helps you build a unary predicate that test tuples according to their element types.
19///
20/// For example:
21///
22/// ```
23/// use tuplez::{tuple, TupleLike, unary_pred};
24///
25/// let tup = tuple!(1, "2", |x: i32| x >= 0);
26/// let result = tup.all(
27///     unary_pred! {
28///         |x: i32| { *x >= 0 }
29///         |x: &str| { !x.is_empty() }
30///         <T: Fn(i32) -> bool> |f: T| { f(1) }
31///     }
32/// );
33/// assert_eq!(result, true);
34/// ```
35///
36/// ## Test tuples in order of their elements
37///
38/// You can create a new tuple with the same number of elements, whose elements are all callable objects that accepts
39/// an immutable reference to an element and returns a `bool` value ([`FnOnce(&T) -> bool`](std::ops::FnOnce)),
40/// then, you can use that tuple as a unary predicate.
41///
42/// For example:
43///
44/// ```
45/// use tuplez::{tuple, TupleLike};
46///
47/// let tup = tuple!(1, 2, "3");
48/// let result = tup.any(
49///     tuple!(
50///         |x: &i32| *x < 0,
51///         |x: &i32| *x > 100,
52///         |x: &&str| *x == "1",
53///     )
54/// );
55/// assert_eq!(result, false);
56/// ```
57///
58/// # Custom unary predicate
59///
60/// NOTE: In general, a unary predicate is equivalent to a mapper that accepts immutable references
61/// to elements and returns a `bool` value. Therefore, instead of [`UnaryPredicate<T>`],
62/// you should implement [`Mapper<&T, Output=bool>`](Mapper) for your custom type, and use it as a unary predicate.
63/// In fact, this is what the [`unary_pred!`](crate::unary_pred!) macro does.
64///
65/// Here is an example:
66///
67/// ```
68/// use std::ops::Range;
69/// use tuplez::{foreach::Mapper, tuple, TupleLike};
70///
71/// #[derive(Clone)]
72/// struct Basis(Range<i32>);
73///
74/// impl Mapper<&i32> for Basis {
75///     type Output = bool;
76///     type NextMapper = Self;
77///     fn map(self, value: &i32) -> (Self::Output, Self::NextMapper) {
78///         (self.0.contains(value), self)
79///     }
80/// }
81///
82/// impl Mapper<&&str> for Basis {
83///     type Output = bool;
84///     type NextMapper = Self;
85///     fn map(self, value: &&str) -> (Self::Output, Self::NextMapper) {
86///         (
87///             value.parse::<i32>().is_ok_and(|s| self.0.contains(&s)),
88///             self,
89///         )
90///     }
91/// }
92///
93/// let basis = Basis(0..5); // Let us assume that `basis` is known only at runtime
94///
95/// let tup = tuple!(1, 2, "3");
96/// let result = tup.all(basis.clone());
97/// assert_eq!(result, true);
98///
99/// let tup = tuple!(-1, 8, "10");
100/// let result = tup.any(basis.clone());
101/// assert_eq!(result, false);
102/// ```
103pub trait UnaryPredicate<'a, T: 'a> {
104    /// Type of next unary predicate to be use.
105    type NextUnaryPredicate;
106
107    /// Test a value with its immutable reference
108    fn test(self, testee: &'a T) -> (bool, Self::NextUnaryPredicate);
109}
110
111impl<'a, T, F> UnaryPredicate<'a, T> for F
112where
113    T: 'a,
114    F: Mapper<&'a T, Output = bool>,
115{
116    type NextUnaryPredicate = <F as Mapper<&'a T>>::NextMapper;
117    fn test(self, testee: &'a T) -> (bool, Self::NextUnaryPredicate) {
118        self.map(testee)
119    }
120}
121
122/// Tests if any element of the tuple matches a predicate.
123///
124/// # The unary predicate `Pred`
125///
126/// For testing [`Tuple<T0, T1, ... Tn>`](crate::Tuple), you need to build a unary predicate,
127/// which needs to implement [`UnaryPredicate<T0>`], and the [`NextUnaryPredicate`](UnaryPredicate::NextUnaryPredicate)
128/// needs to implement [`UnaryPredicate<T1>`], and so on.
129///
130/// See the documentation page of [`UnaryPredicate`] for details.
131pub trait TestAny<Pred>: TupleLike {
132    /// Tests if any element of the tuple matches a predicate.
133    ///
134    /// Check out [`UnaryPredicate`]'s documentation page to learn how to build
135    /// a unary predicate that can be passed to [`any()`](TestAny::any()).
136    ///
137    /// [`any()`](TestAny::any()) is short-circuiting; in other words, it will stop processing as soon as it finds a `true`,
138    /// given that no matter what else happens, the result will also be `true`.
139    ///
140    /// An empty tuple returns `false`.
141    ///
142    /// Hint: The [`TupleLike`] trait provides the [`any()`](TupleLike::any()) method as the wrapper
143    /// for this [`any()`](TestAny::any()) method.
144    ///
145    /// # Example
146    ///
147    /// ```
148    /// use tuplez::{tuple, TupleLike, unary_pred};
149    ///
150    /// let predicate = unary_pred! {
151    ///     |x: i32| { (0..10).contains(x) },
152    ///     |x: &str| { x.parse::<i32>().is_ok() },
153    /// };
154    ///
155    /// let tup = tuple!(100, 2, "world");
156    /// let result = tup.any(predicate);
157    /// assert_eq!(result, true);
158    ///
159    /// let tup = tuple!(-1, 96, "hello");
160    /// let result = tup.any(predicate);
161    /// assert_eq!(result, false);
162    /// ```
163    fn any(&self, predicate: Pred) -> bool;
164}
165
166impl<Pred> TestAny<Pred> for Unit {
167    fn any(&self, _: Pred) -> bool {
168        false
169    }
170}
171
172impl<Pred, First, Other> TestAny<Pred> for Tuple<First, Other>
173where
174    for<'a> Pred: UnaryPredicate<'a, First>,
175    for<'a> Other: TestAny<<Pred as UnaryPredicate<'a, First>>::NextUnaryPredicate>,
176{
177    fn any(&self, predicate: Pred) -> bool {
178        let (res, next) = predicate.test(&self.0);
179        res || TestAny::any(&self.1, next)
180    }
181}
182
183/// Tests if every element of the tuple matches a predicate.
184///
185/// # The unary predicate `Pred`
186///
187/// For testing [`Tuple<T0, T1, ... Tn>`](crate::Tuple), you need to build a unary predicate,
188/// which needs to implement [`UnaryPredicate<T0>`], and the [`NextUnaryPredicate`](UnaryPredicate::NextUnaryPredicate)
189/// needs to implement [`UnaryPredicate<T1>`], and so on.
190///
191/// See the documentation page of [`UnaryPredicate`] for details.
192pub trait TestAll<Pred>: TupleLike {
193    /// Tests if every element of the tuple matches a predicate.
194    ///
195    /// Check out [`UnaryPredicate`]'s documentation page to learn how to build
196    /// a unary predicate that can be passed to [`all()`](TestAll::all()).
197    ///
198    /// [`all()`](TestAll::all()) is short-circuiting; in other words, it will stop processing as soon as it finds a `false`,
199    /// given that no matter what else happens, the result will also be `false`.
200    ///
201    /// An empty tuple returns `true`.
202    ///
203    /// Hint: The [`TupleLike`] trait provides the [`all()`](TupleLike::all()) method as the wrapper
204    /// for this [`all()`](TestAll::all()) method.
205    ///
206    /// # Example
207    ///
208    /// ```
209    /// use tuplez::{tuple, TupleLike, unary_pred};
210    ///
211    /// let predicate = unary_pred! {
212    ///     |x: i32| { (0..10).contains(x) },
213    ///     |x: &str| { x.parse::<i32>().is_ok() },
214    /// };
215    ///
216    /// let tup = tuple!(1, 2, "3");
217    /// let result = tup.all(predicate);
218    /// assert_eq!(result, true);
219    ///
220    /// let tup = tuple!(7, 8, "hello");
221    /// let result = tup.all(predicate);
222    /// assert_eq!(result, false);
223    /// ```
224    fn all(&self, predicate: Pred) -> bool;
225}
226
227impl<Pred> TestAll<Pred> for Unit {
228    fn all(&self, _: Pred) -> bool {
229        true
230    }
231}
232
233impl<Pred, First, Other> TestAll<Pred> for Tuple<First, Other>
234where
235    for<'a> Pred: UnaryPredicate<'a, First>,
236    for<'a> Other: TestAll<<Pred as UnaryPredicate<'a, First>>::NextUnaryPredicate>,
237{
238    fn all(&self, predicate: Pred) -> bool {
239        let (res, next) = predicate.test(&self.0);
240        res && TestAll::all(&self.1, next)
241    }
242}