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 | /// ``` |
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 | { |
83 | fn eval(&self, variable: &P) -> bool { |
84 | self.inner.debug.iter().any(|x| x.borrow() == variable) |
85 | } |
86 | |
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 | { |
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 | { |
111 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
112 | let palette = crate::Palette::current(); |
113 | write!( |
114 | f, |
115 | "{} {} {}" , |
116 | palette.var.paint("var" ), |
117 | palette.description.paint("in" ), |
118 | palette.expected.paint("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 | /// ``` |
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 | { |
189 | 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 | |
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 | { |
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 | { |
220 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
221 | let palette = crate::Palette::current(); |
222 | write!( |
223 | f, |
224 | "{} {} {}" , |
225 | palette.var.paint("var" ), |
226 | palette.description.paint("in" ), |
227 | palette.expected.paint("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 | { |
254 | fn eval(&self, variable: &P) -> bool { |
255 | self.inner.debug.contains(variable) |
256 | } |
257 | |
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 | { |
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 | { |
282 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
283 | let palette = crate::Palette::current(); |
284 | write!( |
285 | f, |
286 | "{} {} {}" , |
287 | palette.var.paint("var" ), |
288 | palette.description.paint("in" ), |
289 | palette.expected.paint("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 | /// ``` |
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 | |