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         let palette = crate::Palette::current();
115         write!(
116             f,
117             "{} {} {}",
118             palette.var.paint("var"),
119             palette.description.paint("in"),
120             palette.expected.paint("values")
121         )
122     }
123 }
124 
125 /// Creates a new predicate that will return `true` when the given `variable` is
126 /// contained with the set of items provided.
127 ///
128 /// Note that this implementation places the fewest restrictions on the
129 /// underlying `Item` type at the expense of having the least performant
130 /// implementation (linear search). If the type to be searched is `Hash + Eq`,
131 /// it is much more efficient to use `HashableInPredicate` and
132 /// `in_hash`. The implementation-specific predicates will be
133 /// deprecated when Rust supports trait specialization.
134 ///
135 /// If you need to optimize this
136 /// - Type is `Ord`, call `sort()` on this predicate.
137 /// - Type is `Hash`, replace `in_iter` with `in_hash`.
138 ///
139 /// # Examples
140 ///
141 /// ```
142 /// use predicates::prelude::*;
143 ///
144 /// let predicate_fn = predicate::in_iter(vec![1, 3, 5]);
145 /// assert_eq!(true, predicate_fn.eval(&1));
146 /// assert_eq!(false, predicate_fn.eval(&2));
147 /// assert_eq!(true, predicate_fn.eval(&3));
148 ///
149 /// let predicate_fn = predicate::in_iter(vec!["a", "c", "e"]);
150 /// assert_eq!(true, predicate_fn.eval("a"));
151 /// assert_eq!(false, predicate_fn.eval("b"));
152 /// assert_eq!(true, predicate_fn.eval("c"));
153 /// ```
in_iter<I, T>(iter: I) -> InPredicate<T> where T: PartialEq + fmt::Debug, I: IntoIterator<Item = T>,154 pub fn in_iter<I, T>(iter: I) -> InPredicate<T>
155 where
156     T: PartialEq + fmt::Debug,
157     I: IntoIterator<Item = T>,
158 {
159     InPredicate {
160         inner: utils::DebugAdapter::new(Vec::from_iter(iter)),
161     }
162 }
163 
164 /// Predicate that returns `true` if `variable` is a member of the pre-defined
165 /// set, otherwise returns `false`.
166 ///
167 /// Note that this implementation requires `Item` to be `Ord`. The
168 /// `InPredicate` uses a less efficient search algorithm but only
169 /// requires `Item` implement `PartialEq`. The implementation-specific
170 /// predicates will be deprecated when Rust supports trait specialization.
171 ///
172 /// This is created by the `predicate::in_iter(...).sort` function.
173 #[derive(Debug, Clone, PartialEq, Eq)]
174 pub struct OrdInPredicate<T>
175 where
176     T: Ord + fmt::Debug,
177 {
178     inner: utils::DebugAdapter<Vec<T>>,
179 }
180 
181 impl<T> Predicate<T> for OrdInPredicate<T>
182 where
183     T: Ord + fmt::Debug,
184 {
eval(&self, variable: &T) -> bool185     fn eval(&self, variable: &T) -> bool {
186         self.inner.debug.binary_search(variable).is_ok()
187     }
188 
find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>>189     fn find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>> {
190         utils::default_find_case(self, expected, variable)
191     }
192 }
193 
194 impl<'a, T> Predicate<T> for OrdInPredicate<&'a T>
195 where
196     T: Ord + fmt::Debug + ?Sized,
197 {
eval(&self, variable: &T) -> bool198     fn eval(&self, variable: &T) -> bool {
199         self.inner.debug.binary_search(&variable).is_ok()
200     }
201 
find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>>202     fn find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>> {
203         utils::default_find_case(self, expected, variable)
204     }
205 }
206 
207 impl<T> reflection::PredicateReflection for OrdInPredicate<T>
208 where
209     T: Ord + fmt::Debug,
210 {
parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a>211     fn parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a> {
212         let params = vec![reflection::Parameter::new("values", &self.inner)];
213         Box::new(params.into_iter())
214     }
215 }
216 
217 impl<T> fmt::Display for OrdInPredicate<T>
218 where
219     T: Ord + fmt::Debug,
220 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result221     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222         let palette = crate::Palette::current();
223         write!(
224             f,
225             "{} {} {}",
226             palette.var.paint("var"),
227             palette.description.paint("in"),
228             palette.expected.paint("values")
229         )
230     }
231 }
232 
233 /// Predicate that returns `true` if `variable` is a member of the pre-defined
234 /// `HashSet`, otherwise returns `false`.
235 ///
236 /// Note that this implementation requires `Item` to be `Hash + Eq`. The
237 /// `InPredicate` uses a less efficient search algorithm but only
238 /// requires `Item` implement `PartialEq`. The implementation-specific
239 /// predicates will be deprecated when Rust supports trait specialization.
240 ///
241 /// This is created by the `predicate::in_hash` function.
242 #[derive(Debug, Clone, PartialEq, Eq)]
243 pub struct HashableInPredicate<T>
244 where
245     T: Hash + Eq + fmt::Debug,
246 {
247     inner: utils::DebugAdapter<HashSet<T>>,
248 }
249 
250 impl<T> Predicate<T> for HashableInPredicate<T>
251 where
252     T: Hash + Eq + fmt::Debug,
253 {
eval(&self, variable: &T) -> bool254     fn eval(&self, variable: &T) -> bool {
255         self.inner.debug.contains(variable)
256     }
257 
find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>>258     fn find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>> {
259         utils::default_find_case(self, expected, variable)
260     }
261 }
262 
263 impl<'a, T> Predicate<T> for HashableInPredicate<&'a T>
264 where
265     T: Hash + Eq + fmt::Debug + ?Sized,
266 {
eval(&self, variable: &T) -> bool267     fn eval(&self, variable: &T) -> bool {
268         self.inner.debug.contains(&variable)
269     }
270 
find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>>271     fn find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>> {
272         utils::default_find_case(self, expected, variable)
273     }
274 }
275 
276 impl<T> reflection::PredicateReflection for HashableInPredicate<T>
277 where
278     T: Hash + Eq + fmt::Debug,
279 {
parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a>280     fn parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a> {
281         let params = vec![reflection::Parameter::new("values", &self.inner)];
282         Box::new(params.into_iter())
283     }
284 }
285 
286 impl<T> fmt::Display for HashableInPredicate<T>
287 where
288     T: Hash + Eq + fmt::Debug,
289 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result290     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291         let palette = crate::Palette::current();
292         write!(
293             f,
294             "{} {} {}",
295             palette.var.paint("var"),
296             palette.description.paint("in"),
297             palette.expected.paint("values")
298         )
299     }
300 }
301 
302 /// Creates a new predicate that will return `true` when the given `variable` is
303 /// contained with the set of items provided.
304 ///
305 /// Note that this implementation requires `Item` to be `Hash + Eq`. The
306 /// `InPredicate` uses a less efficient search algorithm but only
307 /// requires `Item` implement `PartialEq`. The implementation-specific
308 /// predicates will be deprecated when Rust supports trait specialization.
309 ///
310 /// # Examples
311 ///
312 /// ```
313 /// use predicates::prelude::*;
314 ///
315 /// let predicate_fn = predicate::in_hash(vec![1, 3, 5]);
316 /// assert_eq!(true, predicate_fn.eval(&1));
317 /// assert_eq!(false, predicate_fn.eval(&2));
318 /// assert_eq!(true, predicate_fn.eval(&3));
319 ///
320 /// let predicate_fn = predicate::in_hash(vec!["a", "c", "e"]);
321 /// assert_eq!(true, predicate_fn.eval("a"));
322 /// assert_eq!(false, predicate_fn.eval("b"));
323 /// assert_eq!(true, predicate_fn.eval("c"));
324 /// ```
in_hash<I, T>(iter: I) -> HashableInPredicate<T> where T: Hash + Eq + fmt::Debug, I: IntoIterator<Item = T>,325 pub fn in_hash<I, T>(iter: I) -> HashableInPredicate<T>
326 where
327     T: Hash + Eq + fmt::Debug,
328     I: IntoIterator<Item = T>,
329 {
330     HashableInPredicate {
331         inner: utils::DebugAdapter::new(HashSet::from_iter(iter)),
332     }
333 }
334