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