predicates/
iter.rs

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::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/// ```
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::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)]
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::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/// ```
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}