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