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
11use std::collections::HashSet;
12use std::fmt;
13use std::hash::Hash;
14use std::iter::FromIterator;
15
16use crate::reflection;
17use crate::utils;
18use 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)]
30pub struct InPredicate<T>
31where
32 T: PartialEq + fmt::Debug,
33{
34 inner: utils::DebugAdapter<Vec<T>>,
35}
36
37impl<T> InPredicate<T>
38where
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
78impl<P, T> Predicate<P> for InPredicate<T>
79where
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
97impl<T> reflection::PredicateReflection for InPredicate<T>
98where
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
107impl<T> fmt::Display for InPredicate<T>
108where
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/// ```
157pub fn in_iter<I, T>(iter: I) -> InPredicate<T>
158where
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)]
177pub struct OrdInPredicate<T>
178where
179 T: Ord + fmt::Debug,
180{
181 inner: utils::DebugAdapter<Vec<T>>,
182}
183
184impl<P, T> Predicate<P> for OrdInPredicate<T>
185where
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
206impl<T> reflection::PredicateReflection for OrdInPredicate<T>
207where
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
216impl<T> fmt::Display for OrdInPredicate<T>
217where
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)]
242pub struct HashableInPredicate<T>
243where
244 T: Hash + Eq + fmt::Debug,
245{
246 inner: utils::DebugAdapter<HashSet<T>>,
247}
248
249impl<P, T> Predicate<P> for HashableInPredicate<T>
250where
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
268impl<T> reflection::PredicateReflection for HashableInPredicate<T>
269where
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
278impl<T> fmt::Display for HashableInPredicate<T>
279where
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/// ```
322pub fn in_hash<I, T>(iter: I) -> HashableInPredicate<T>
323where
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