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 `Predicate`s for comparisons of membership in a set.
10 
11 use std::collections::HashSet;
12 use std::fmt;
13 use std::hash::Hash;
14 use std::iter::FromIterator;
15 
16 use crate::reflection;
17 use crate::utils;
18 use crate::Predicate;
19 
20 /// Predicate that returns `true` if `variable` is a member of the pre-defined
21 /// set, otherwise returns `false`.
22 ///
23 /// Note that this implementation places the fewest restrictions on the
24 /// underlying `Item` type at the expense of having the least performant
25 /// implementation (linear search). If the type to be searched is `Hash + Eq`,
26 /// it is much more efficient to use `HashableInPredicate` and
27 /// `in_hash`. The implementation-specific predicates will be
28 /// deprecated when Rust supports trait specialization.
29 #[derive(Debug, Clone, PartialEq, Eq)]
30 pub struct InPredicate<T>
31 where
32     T: PartialEq + fmt::Debug,
33 {
34     inner: utils::DebugAdapter<Vec<T>>,
35 }
36 
37 impl<T> InPredicate<T>
38 where
39     T: Ord + fmt::Debug,
40 {
41     /// Creates a new predicate that will return `true` when the given `variable` is
42     /// contained with the set of items provided.
43     ///
44     /// Note that this implementation requires `Item` to be `Ord`. The
45     /// `InPredicate` uses a less efficient search algorithm but only
46     /// requires `Item` implement `PartialEq`. The implementation-specific
47     /// predicates will be deprecated when Rust supports trait specialization.
48     ///
49     /// # Examples
50     ///
51     /// ```
52     /// use predicates::prelude::*;
53     ///
54     /// let predicate_fn = predicate::in_iter(vec![1, 3, 5]).sort();
55     /// assert_eq!(true, predicate_fn.eval(&1));
56     /// assert_eq!(false, predicate_fn.eval(&2));
57     /// assert_eq!(true, predicate_fn.eval(&3));
58     ///
59     /// let predicate_fn = predicate::in_iter(vec!["a", "c", "e"]).sort();
60     /// assert_eq!(true, predicate_fn.eval("a"));
61     /// assert_eq!(false, predicate_fn.eval("b"));
62     /// assert_eq!(true, predicate_fn.eval("c"));
63     /// ```
sort(self) -> OrdInPredicate<T>64     pub fn sort(self) -> OrdInPredicate<T> {
65         let mut items = self.inner.debug;
66         items.sort();
67         OrdInPredicate {
68             inner: utils::DebugAdapter::new(items),
69         }
70     }
71 }
72 
73 impl<T> Predicate<T> for InPredicate<T>
74 where
75     T: PartialEq + fmt::Debug,
76 {
eval(&self, variable: &T) -> bool77     fn eval(&self, variable: &T) -> bool {
78         self.inner.debug.contains(variable)
79     }
80 
find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>>81     fn find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>> {
82         utils::default_find_case(self, expected, variable)
83     }
84 }
85 
86 impl<'a, T> Predicate<T> for InPredicate<&'a T>
87 where
88     T: PartialEq + fmt::Debug + ?Sized,
89 {
eval(&self, variable: &T) -> bool90     fn eval(&self, variable: &T) -> bool {
91         self.inner.debug.contains(&variable)
92     }
93 
find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>>94     fn find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>> {
95         utils::default_find_case(self, expected, variable)
96     }
97 }
98 
99 impl<T> reflection::PredicateReflection for InPredicate<T>
100 where
101     T: PartialEq + fmt::Debug,
102 {
parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a>103     fn parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a> {
104         let params = vec![reflection::Parameter::new("values", &self.inner)];
105         Box::new(params.into_iter())
106     }
107 }
108 
109 impl<T> fmt::Display for InPredicate<T>
110 where
111     T: PartialEq + fmt::Debug,
112 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result113     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114         write!(f, "var in values")
115     }
116 }
117 
118 /// Creates a new predicate that will return `true` when the given `variable` is
119 /// contained with the set of items provided.
120 ///
121 /// Note that this implementation places the fewest restrictions on the
122 /// underlying `Item` type at the expense of having the least performant
123 /// implementation (linear search). If the type to be searched is `Hash + Eq`,
124 /// it is much more efficient to use `HashableInPredicate` and
125 /// `in_hash`. The implementation-specific predicates will be
126 /// deprecated when Rust supports trait specialization.
127 ///
128 /// If you need to optimize this
129 /// - Type is `Ord`, call `sort()` on this predicate.
130 /// - Type is `Hash`, replace `in_iter` with `in_hash`.
131 ///
132 /// # Examples
133 ///
134 /// ```
135 /// use predicates::prelude::*;
136 ///
137 /// let predicate_fn = predicate::in_iter(vec![1, 3, 5]);
138 /// assert_eq!(true, predicate_fn.eval(&1));
139 /// assert_eq!(false, predicate_fn.eval(&2));
140 /// assert_eq!(true, predicate_fn.eval(&3));
141 ///
142 /// let predicate_fn = predicate::in_iter(vec!["a", "c", "e"]);
143 /// assert_eq!(true, predicate_fn.eval("a"));
144 /// assert_eq!(false, predicate_fn.eval("b"));
145 /// assert_eq!(true, predicate_fn.eval("c"));
146 /// ```
in_iter<I, T>(iter: I) -> InPredicate<T> where T: PartialEq + fmt::Debug, I: IntoIterator<Item = T>,147 pub fn in_iter<I, T>(iter: I) -> InPredicate<T>
148 where
149     T: PartialEq + fmt::Debug,
150     I: IntoIterator<Item = T>,
151 {
152     InPredicate {
153         inner: utils::DebugAdapter::new(Vec::from_iter(iter)),
154     }
155 }
156 
157 /// Predicate that returns `true` if `variable` is a member of the pre-defined
158 /// set, otherwise returns `false`.
159 ///
160 /// Note that this implementation requires `Item` to be `Ord`. The
161 /// `InPredicate` uses a less efficient search algorithm but only
162 /// requires `Item` implement `PartialEq`. The implementation-specific
163 /// predicates will be deprecated when Rust supports trait specialization.
164 ///
165 /// This is created by the `predicate::in_iter(...).sort` function.
166 #[derive(Debug, Clone, PartialEq, Eq)]
167 pub struct OrdInPredicate<T>
168 where
169     T: Ord + fmt::Debug,
170 {
171     inner: utils::DebugAdapter<Vec<T>>,
172 }
173 
174 impl<T> Predicate<T> for OrdInPredicate<T>
175 where
176     T: Ord + fmt::Debug,
177 {
eval(&self, variable: &T) -> bool178     fn eval(&self, variable: &T) -> bool {
179         self.inner.debug.binary_search(variable).is_ok()
180     }
181 
find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>>182     fn find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>> {
183         utils::default_find_case(self, expected, variable)
184     }
185 }
186 
187 impl<'a, T> Predicate<T> for OrdInPredicate<&'a T>
188 where
189     T: Ord + fmt::Debug + ?Sized,
190 {
eval(&self, variable: &T) -> bool191     fn eval(&self, variable: &T) -> bool {
192         self.inner.debug.binary_search(&variable).is_ok()
193     }
194 
find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>>195     fn find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>> {
196         utils::default_find_case(self, expected, variable)
197     }
198 }
199 
200 impl<T> reflection::PredicateReflection for OrdInPredicate<T>
201 where
202     T: Ord + fmt::Debug,
203 {
parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a>204     fn parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a> {
205         let params = vec![reflection::Parameter::new("values", &self.inner)];
206         Box::new(params.into_iter())
207     }
208 }
209 
210 impl<T> fmt::Display for OrdInPredicate<T>
211 where
212     T: Ord + fmt::Debug,
213 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result214     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215         write!(f, "var in values")
216     }
217 }
218 
219 /// Predicate that returns `true` if `variable` is a member of the pre-defined
220 /// `HashSet`, otherwise returns `false`.
221 ///
222 /// Note that this implementation requires `Item` to be `Hash + Eq`. The
223 /// `InPredicate` uses a less efficient search algorithm but only
224 /// requires `Item` implement `PartialEq`. The implementation-specific
225 /// predicates will be deprecated when Rust supports trait specialization.
226 ///
227 /// This is created by the `predicate::in_hash` function.
228 #[derive(Debug, Clone, PartialEq, Eq)]
229 pub struct HashableInPredicate<T>
230 where
231     T: Hash + Eq + fmt::Debug,
232 {
233     inner: utils::DebugAdapter<HashSet<T>>,
234 }
235 
236 impl<T> Predicate<T> for HashableInPredicate<T>
237 where
238     T: Hash + Eq + fmt::Debug,
239 {
eval(&self, variable: &T) -> bool240     fn eval(&self, variable: &T) -> bool {
241         self.inner.debug.contains(variable)
242     }
243 
find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>>244     fn find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>> {
245         utils::default_find_case(self, expected, variable)
246     }
247 }
248 
249 impl<'a, T> Predicate<T> for HashableInPredicate<&'a T>
250 where
251     T: Hash + Eq + fmt::Debug + ?Sized,
252 {
eval(&self, variable: &T) -> bool253     fn eval(&self, variable: &T) -> bool {
254         self.inner.debug.contains(&variable)
255     }
256 
find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>>257     fn find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>> {
258         utils::default_find_case(self, expected, variable)
259     }
260 }
261 
262 impl<T> reflection::PredicateReflection for HashableInPredicate<T>
263 where
264     T: Hash + Eq + fmt::Debug,
265 {
parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a>266     fn parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a> {
267         let params = vec![reflection::Parameter::new("values", &self.inner)];
268         Box::new(params.into_iter())
269     }
270 }
271 
272 impl<T> fmt::Display for HashableInPredicate<T>
273 where
274     T: Hash + Eq + fmt::Debug,
275 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result276     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277         write!(f, "var in values")
278     }
279 }
280 
281 /// Creates a new predicate that will return `true` when the given `variable` is
282 /// contained with the set of items provided.
283 ///
284 /// Note that this implementation requires `Item` to be `Hash + Eq`. The
285 /// `InPredicate` uses a less efficient search algorithm but only
286 /// requires `Item` implement `PartialEq`. The implementation-specific
287 /// predicates will be deprecated when Rust supports trait specialization.
288 ///
289 /// # Examples
290 ///
291 /// ```
292 /// use predicates::prelude::*;
293 ///
294 /// let predicate_fn = predicate::in_hash(vec![1, 3, 5]);
295 /// assert_eq!(true, predicate_fn.eval(&1));
296 /// assert_eq!(false, predicate_fn.eval(&2));
297 /// assert_eq!(true, predicate_fn.eval(&3));
298 ///
299 /// let predicate_fn = predicate::in_hash(vec!["a", "c", "e"]);
300 /// assert_eq!(true, predicate_fn.eval("a"));
301 /// assert_eq!(false, predicate_fn.eval("b"));
302 /// assert_eq!(true, predicate_fn.eval("c"));
303 /// ```
in_hash<I, T>(iter: I) -> HashableInPredicate<T> where T: Hash + Eq + fmt::Debug, I: IntoIterator<Item = T>,304 pub fn in_hash<I, T>(iter: I) -> HashableInPredicate<T>
305 where
306     T: Hash + Eq + fmt::Debug,
307     I: IntoIterator<Item = T>,
308 {
309     HashableInPredicate {
310         inner: utils::DebugAdapter::new(HashSet::from_iter(iter)),
311     }
312 }
313