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