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}