galvanic_assert/matchers/
collection.rs

1/* Copyright 2017 Christopher Bacher
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16//! The collection module contains matchers for asserting properties of collections and iterators.
17
18use std::fmt::Debug;
19use super::super::*;
20
21use std::iter::FromIterator;
22
23/// Matches if the asserted collection contains *all and only* the expected elements in any order.
24pub struct ContainsInAnyOrder<T> {
25    expected_elements: Vec<T>
26}
27
28/// Matches if the asserted collection contains *all and only* of the expected elements in any order.
29///
30/// #Examples
31/// ```rust
32/// # #[macro_use] extern crate galvanic_assert;
33/// use galvanic_assert::matchers::collection::*;
34/// # fn main() {
35/// assert_that!(&vec![1,2,3,4,5,6], contains_in_any_order(vec![2,4,1,5,3,6]));
36/// assert_that!(
37///     // 6 is missing
38///     assert_that!(&vec![1,2,3,4,5,6], contains_in_any_order(vec![2,4,1,5,3])),
39///     panics
40/// );
41/// assert_that!(
42///     // 7 is added
43///     assert_that!(&vec![1,2,3,4,5,6], contains_in_any_order(vec![2,4,1,5,3,6,7])),
44///     panics
45/// );
46/// # }
47pub fn contains_in_any_order<'a,T:'a,I:'a,J:'a>(expected_elements: I) -> Box<Matcher<'a,J> + 'a>
48where T: PartialEq + Debug,
49      I: IntoIterator<Item=T>,
50      J: IntoIterator<Item=T>,
51      ContainsInAnyOrder<T>: Matcher<'a,J> {
52    Box::new(ContainsInAnyOrder {
53        expected_elements: expected_elements.into_iter().collect()
54    })
55}
56
57impl<'a,T,I> Matcher<'a,I> for ContainsInAnyOrder<T>
58where T: PartialEq + Debug + 'a,
59      &'a I: IntoIterator<Item=&'a T> + Debug + 'a {
60    fn check(&self, actual: &'a I) -> MatchResult {
61        let repr = format!("{:?}", actual);
62        let builder = MatchResultBuilder::for_("contains_in_any_order");
63        let mut expected_elements = Vec::from_iter(self.expected_elements.iter());
64
65        for ref element in actual.into_iter() {
66            let maybe_pos = expected_elements.iter()
67                                             .position(|candidate| element == candidate);
68            if let Some(idx) = maybe_pos {
69                expected_elements.remove(idx);
70            } else {
71                return builder.failed_because(
72                    &format!("{} contains an unexpected element: {:?}", repr, element)
73                );
74            }
75        }
76
77        if !expected_elements.is_empty() {
78            builder.failed_because(
79                &format!("{} did not contain the following elements: {:?}", repr, expected_elements)
80            )
81        } else { builder.matched() }
82    }
83}
84
85/// Matches if the asserted collection contains *all and only* of the expected elements in the given order.
86pub struct ContainsInOrder<T> {
87    expected_elements: Vec<T>
88}
89
90/// Matches if the asserted collection contains *all and only* of the expected elements in the given order.
91///
92/// #Examples
93/// ```rust
94/// # #[macro_use] extern crate galvanic_assert;
95/// use galvanic_assert::matchers::collection::*;
96/// # fn main() {
97/// assert_that!(&vec![1,2,3,4,5,6], contains_in_order(vec![1,2,3,4,5,6]));
98/// assert_that!(
99///     // 6 is missing
100///     assert_that!(&vec![1,2,3,4,5,6], contains_in_order(vec![1,2,3,4,5])),
101///     panics
102/// );
103/// assert_that!(
104///     // 7 is added
105///     assert_that!(&vec![1,2,3,4,5,6], contains_in_order(vec![1,2,3,4,5,6,7])),
106///     panics
107/// );
108/// # }
109pub fn contains_in_order<'a,T:'a,I:'a,J:'a>(expected_elements: I) -> Box<Matcher<'a,J> + 'a>
110where T: PartialEq + Debug,
111      I: IntoIterator<Item=T>,
112      J: IntoIterator<Item=T>,
113      ContainsInOrder<T>: Matcher<'a,J> {
114    Box::new(ContainsInOrder {
115        expected_elements: expected_elements.into_iter().collect()
116    })
117}
118
119impl<'a, T, I:'a> Matcher<'a,I> for ContainsInOrder<T>
120where T: PartialEq + Debug + 'a,
121      &'a I: IntoIterator<Item=&'a T> + Debug + 'a {
122    fn check(&self, actual: &'a I) -> MatchResult {
123        let builder = MatchResultBuilder::for_("contains_in_order");
124        let actual_list: Vec<_> = actual.into_iter().collect();
125
126        if actual_list.len() > self.expected_elements.len() {
127            return builder.failed_because(
128                &format!("The expected list is shorter than the actual list by {} elements",
129                         actual_list.len() - self.expected_elements.len())
130            );
131        }
132
133        if actual_list.len() < self.expected_elements.len() {
134            return builder.failed_because(
135                &format!("The actual list is shorter than the expected list by {} elements",
136                         self.expected_elements.len() - actual_list.len())
137            );
138        }
139
140        let nonmatching: Vec<_> = actual_list.into_iter()
141                                             .zip(self.expected_elements.iter())
142                                             .filter(|&(act, exp)| act != exp)
143                                             .collect();
144        if !nonmatching.is_empty() {
145            builder.failed_because(
146                &format!("the following actual/expected pairs do not match: {:?}", nonmatching)
147            )
148        } else { builder.matched() }
149    }
150}
151
152/// Matches if the asserted collection contains *all* (possibly more) of the expected elements.
153pub struct ContainsSubset<T> {
154    expected_elements: Vec<T>
155}
156
157/// Matches if the asserted collection contains *all* (possibly more) of the expected elements.
158///
159/// #Examples
160/// ```rust
161/// # #[macro_use] extern crate galvanic_assert;
162/// use galvanic_assert::matchers::collection::*;
163/// # fn main() {
164/// assert_that!(&vec![1,2,3,4,5,6], contains_subset(vec![3,1,2,4]));
165/// # }
166pub fn contains_subset<'a,T:'a,I:'a,J:'a>(expected_elements: I) -> Box<Matcher<'a,J> + 'a>
167where T: PartialEq + Debug,
168      I: IntoIterator<Item=T>,
169      J: IntoIterator<Item=T>,
170      ContainsSubset<T>: Matcher<'a,J> {
171    Box::new(ContainsSubset {
172        expected_elements: expected_elements.into_iter().collect()
173    })
174}
175
176impl<'a, T, I:'a> Matcher<'a,I> for ContainsSubset<T>
177where T: PartialEq + Debug + 'a,
178      &'a I: IntoIterator<Item=&'a T> + Debug + 'a {
179    fn check(&self, actual: &'a I) -> MatchResult {
180        let repr = format!("{:?}", actual);
181        let builder = MatchResultBuilder::for_("contains_subset");
182        let mut expected_elements = Vec::from_iter(self.expected_elements.iter());
183
184        for element in actual.into_iter() {
185            let maybe_pos = expected_elements.iter()
186                                             .position(|candidate| element == *candidate);
187            if let Some(idx) = maybe_pos {
188                expected_elements.remove(idx);
189            }
190        }
191
192        if !expected_elements.is_empty() {
193            builder.failed_because(
194                &format!("{} did not contain the following elements: {:?}", repr, expected_elements)
195            )
196        } else { builder.matched() }
197    }
198}
199
200/// Matches if the asserted (single) value is contained in the expected elements.
201pub struct ContainedIn<T> {
202    expected_to_contain: Vec<T>
203}
204
205/// Matches if the asserted (single) value is contained in the expected elements.
206///
207/// #Examples
208/// ```rust
209/// # #[macro_use] extern crate galvanic_assert;
210/// use galvanic_assert::matchers::collection::*;
211/// # fn main() {
212/// assert_that!(&5, contained_in(vec![1,2,3,4,5,6,7,8]));
213/// # }
214pub fn contained_in<'a,T:'a,I>(expected_to_contain: I) -> Box<Matcher<'a,T> + 'a>
215where T: PartialEq + Debug,
216      I: IntoIterator<Item=T> {
217    Box::new(ContainedIn {
218        expected_to_contain: expected_to_contain.into_iter().collect()
219    })
220}
221
222impl<'a,T> Matcher<'a,T> for ContainedIn<T>
223where T: PartialEq + Debug + 'a  {
224    fn check(&self, element: &T) -> MatchResult {
225        let builder = MatchResultBuilder::for_("containd_in");
226        if let None = self.expected_to_contain.iter().position(|e| e == element) {
227            builder.failed_because(
228                &format!("{:?} does not contain: {:?}", self.expected_to_contain, element)
229            )
230        } else { builder.matched() }
231    }
232}
233
234/// Matches if the elements in the asserted collection are sorted weakly monotone according to the given `predicate` in the expected order.
235///
236/// The `predicate` is applied to all consecutive pairs of elements and returns the `Ordering` of the pair.
237/// The ordering is allowed to be weakly monotone, i.e., equal elements are allowed to follow each other.
238/// An empty collection is assumed to be always sorted.
239///
240/// #Examples
241/// ```rust
242/// # #[macro_use] extern crate galvanic_assert;
243/// use galvanic_assert::matchers::collection::*;
244/// use std::cmp::Ordering;
245/// # fn main() {
246/// assert_that!(&vec![1,2,2,3,3,4,5,6], sorted_by(|a: &i32, b: &i32| a.cmp(b), Ordering::Less));
247/// # }
248pub fn sorted_by<'a,T,I,P>(predicate: P, expected_ordering: std::cmp::Ordering) -> Box<Fn(&'a I) -> MatchResult>
249where &'a I: IntoIterator<Item=&'a T> + 'a,
250      T: Ord + Debug + 'a,
251      P: Fn(&'a T,&'a T) -> std::cmp::Ordering + 'static {
252    Box::new(move |elements: &'a I| {
253        let builder = MatchResultBuilder::for_("sorted_by");
254        let mut iter = elements.into_iter();
255        let maybe_prev = iter.next();
256
257        if maybe_prev.is_none() { return builder.matched() }
258        let mut prev = maybe_prev.unwrap();
259
260        for cur in iter {
261            let ordering = predicate(&prev, &cur);
262            if ordering != std::cmp::Ordering::Equal
263                      && expected_ordering != ordering  {
264                return builder.failed_because(
265                    &format!("ordering is not monotone: predicate({:?}, {:?}) != {:?}",
266                             prev, cur, expected_ordering)
267                );
268            }
269            prev = cur;
270        }
271        builder.matched()
272    })
273}
274
275/// Matches if the elements in the asserted collection are sorted strictly monotone according to the given `predicate` in the expected order`.
276///
277/// The `predicate` is applied to all consecutive pairs of elements and returns the `Ordering` of the pair.
278/// The ordering is allowed to be weakly monotone, i.e., equal elements are allowed to follow each other.
279/// An empty collection is assumed to be always sorted.
280///
281/// #Examples
282/// ```rust
283/// # #[macro_use] extern crate galvanic_assert;
284/// use galvanic_assert::matchers::collection::*;
285/// use std::cmp::Ordering;
286/// # fn main() {
287/// assert_that!(&vec![1,2,3,4,5,6], sorted_strictly_by(|a: &i32, b: &i32| a.cmp(b), Ordering::Less));
288/// # }
289pub fn sorted_strictly_by<'a,T,I,P>(predicate: P, expected_ordering: std::cmp::Ordering) -> Box<Fn(&'a I) -> MatchResult>
290where &'a I: IntoIterator<Item=&'a T> + 'a,
291      T: Ord + Debug + 'a,
292      P: Fn(&'a T,&'a T) -> std::cmp::Ordering + 'static {
293    Box::new(move |elements: &'a I| {
294        let builder = MatchResultBuilder::for_("sorted_strictly_by");
295        let mut iter = elements.into_iter();
296        let maybe_prev = iter.next();
297
298        if maybe_prev.is_none() { return builder.matched() }
299        let mut prev = maybe_prev.unwrap();
300
301        for cur in iter {
302            let ordering = predicate(&prev, &cur);
303            if expected_ordering != ordering  {
304                return builder.failed_because(
305                    &format!("ordering is not strictly monotone: predicate({:?}, {:?}) != {:?}", prev, cur, expected_ordering)
306                );
307            }
308            prev = cur;
309        }
310        builder.matched()
311    })
312}
313
314/// Matches if the elements in the asserted collection are sorted weakly monotone according to the given `predicate` in any order.
315///
316/// The `predicate` is applied to all consecutive pairs of elements and returns the `Ordering` of the pair.
317/// The first `Ordering` different to `Ordering::Equal` defines the expected order of the collection.
318/// The ordering is allowed to be weakly monotone, i.e., equal elements are allowed to follow each other.
319/// An empty collection is assumed to be always sorted.
320///
321/// #Examples
322/// ```rust
323/// # #[macro_use] extern crate galvanic_assert;
324/// use galvanic_assert::matchers::collection::*;
325/// # fn main() {
326/// assert_that!(&vec![5,4,3,3,2,1,1], sorted_by_in_any_order(|a: &i32, b: &i32| a.cmp(b)));
327/// assert_that!(&vec![1,1,2,3,3,4,5], sorted_by_in_any_order(|a: &i32, b: &i32| a.cmp(b)));
328/// # }
329pub fn sorted_by_in_any_order<'a,T,I,P>(predicate: P) -> Box<Fn(&'a I) -> MatchResult>
330where &'a I: IntoIterator<Item=&'a T> + 'a,
331      T: Ord + Debug + 'a,
332      P: Fn(&'a T,&'a T) -> std::cmp::Ordering + 'static {
333    Box::new(move |elements: &'a I| {
334        let builder = MatchResultBuilder::for_("sorted_by_in_any_order");
335        let mut iter = elements.into_iter();
336        let mut expected_ordering: Option<std::cmp::Ordering> = None;
337        let maybe_prev = iter.next();
338        if maybe_prev.is_none() {
339            return MatchResult::Matched { name: "sorted_by_in_any_order".to_owned() };
340        }
341        let mut prev = maybe_prev.unwrap();
342
343        for cur in iter {
344            let ordering = predicate(&prev, &cur);
345            if expected_ordering == None && ordering != std::cmp::Ordering::Equal {
346                expected_ordering = Some(ordering);
347            } else if ordering != std::cmp::Ordering::Equal
348                      && expected_ordering.unwrap() != ordering  {
349                return builder.failed_because(
350                    &format!("ordering is not monotone: predicate({:?}, {:?}) != {:?}",
351                             prev, cur, expected_ordering.unwrap())
352                );
353            }
354            prev = cur;
355        }
356        builder.matched()
357    })
358}
359
360/// Matches if the elements in the asserted collection are sorted strictly monotone according to the given `predicate` in any order.
361///
362/// The `predicate` is applied to all consecutive pairs of elements and returns the `Ordering` of the pair.
363/// The first `Ordering` different to `Ordering::Equal` defines the expected order of the collection.
364/// The ordering is allowed to be weakly monotone, i.e., equal elements are allowed to follow each other.
365/// An empty collection is assumed to be always sorted.
366///
367/// #Examples
368/// ```rust
369/// # #[macro_use] extern crate galvanic_assert;
370/// use galvanic_assert::matchers::collection::*;
371/// # fn main() {
372/// assert_that!(&vec![5,4,3,2,1], sorted_strictly_by_in_any_order(|a: &i32, b: &i32| a.cmp(b)));
373/// assert_that!(&vec![1,2,3,4,5], sorted_strictly_by_in_any_order(|a: &i32, b: &i32| a.cmp(b)));
374/// # }
375pub fn sorted_strictly_by_in_any_order<'a,T,I,P>(predicate: P) -> Box<Fn(&'a I) -> MatchResult>
376where &'a I: IntoIterator<Item=&'a T> + 'a,
377      T: Ord + Debug + 'a,
378      P: Fn(&'a T,&'a T) -> std::cmp::Ordering + 'static {
379    Box::new(move |elements: &'a I| {
380        let builder = MatchResultBuilder::for_("sorted_strictly_by_in_any_order");
381        let mut iter = elements.into_iter();
382        let mut expected_ordering: Option<std::cmp::Ordering> = None;
383        let maybe_prev = iter.next();
384        if maybe_prev.is_none() {
385            return builder.matched();
386        }
387        let mut prev = maybe_prev.unwrap();
388
389        for cur in iter {
390            let ordering = predicate(&prev, &cur);
391            if ordering == std::cmp::Ordering::Equal {
392                return builder.failed_because(
393                    &format!("ordering is not strictly monotone: predicate({:?}, {:?}) = {:?}",
394                             prev, cur, ordering)
395                );
396            }
397            if expected_ordering == None {
398                expected_ordering = Some(ordering);
399            } else if expected_ordering.unwrap() != ordering  {
400                return builder.failed_because(
401                    &format!("ordering is not strictly monotone: predicate({:?}, {:?}) != {:?}",
402                             prev, cur, expected_ordering.unwrap())
403                );
404            }
405            prev = cur;
406        }
407        builder.matched()
408    })
409}
410
411/// Matches if the asserted collection is sorted weakly ascending.
412///
413/// An empty collection is assumed to be always sorted.
414///
415/// #Examples
416/// ```rust
417/// # #[macro_use] extern crate galvanic_assert;
418/// use galvanic_assert::matchers::collection::*;
419/// # fn main() {
420/// assert_that!(&vec![1,2,2,3,4,4,5], sorted_ascending());
421/// # }
422pub fn sorted_ascending<'a,T,I>() -> Box<Fn(&'a I) -> MatchResult>
423where &'a I: IntoIterator<Item=&'a T> + 'a,
424      T: Ord + Debug + 'a {
425    sorted_by(|a: &T, b: &T| a.cmp(b), std::cmp::Ordering::Less)
426}
427
428/// Matches if the asserted collection is sorted strictly ascending.
429///
430/// An empty collection is assumed to be always sorted.
431///
432/// #Examples
433/// ```rust
434/// # #[macro_use] extern crate galvanic_assert;
435/// use galvanic_assert::matchers::collection::*;
436/// # fn main() {
437/// assert_that!(&vec![1,2,3,4,5], sorted_strictly_ascending());
438/// # }
439pub fn sorted_strictly_ascending<'a,T,I>() -> Box<Fn(&'a I) -> MatchResult>
440where &'a I: IntoIterator<Item=&'a T> + 'a,
441      T: Ord + Debug + 'a {
442    sorted_strictly_by(|a: &T, b: &T| a.cmp(b), std::cmp::Ordering::Less)
443}
444
445/// Matches if the asserted collection is sorted weakly descending.
446///
447/// An empty collection is assumed to be always sorted.
448///
449/// #Examples
450/// ```rust
451/// # #[macro_use] extern crate galvanic_assert;
452/// use galvanic_assert::matchers::collection::*;
453/// # fn main() {
454/// assert_that!(&vec![5,4,4,3,3,2,1], sorted_descending());
455/// # }
456pub fn sorted_descending<'a,T,I>() -> Box<Fn(&'a I) -> MatchResult>
457where &'a I: IntoIterator<Item=&'a T> + 'a,
458      T: Ord + Debug + 'a {
459    sorted_by(|a: &T, b: &T| a.cmp(b), std::cmp::Ordering::Greater)
460}
461
462/// Matches if the asserted collection is sorted strictly descending.
463///
464/// An empty collection is assumed to be always sorted.
465///
466/// #Examples
467/// ```rust
468/// # #[macro_use] extern crate galvanic_assert;
469/// use galvanic_assert::matchers::collection::*;
470/// # fn main() {
471/// assert_that!(&vec![5,4,3,2,1], sorted_strictly_descending());
472/// # }
473pub fn sorted_strictly_descending<'a,T,I>() -> Box<Fn(&'a I) -> MatchResult>
474where &'a I: IntoIterator<Item=&'a T> + 'a,
475      T: Ord + Debug + 'a {
476    sorted_strictly_by(|a: &T, b: &T| a.cmp(b), std::cmp::Ordering::Greater)
477}
478
479/// Matches if all elements in the asserted collection satisfy the given `predicate`.
480///
481/// An empty collection always satisfies this matcher as all (=no) element satisfies the predicate.
482///
483/// #Examples
484/// ```rust
485/// # #[macro_use] extern crate galvanic_assert;
486/// use galvanic_assert::matchers::collection::*;
487/// # fn main() {
488/// assert_that!(&vec![1,2,3,4,5], all_elements_satisfy(|&a| 0 <= a && a < 100));
489/// # }
490pub fn all_elements_satisfy<'a,T,I,P>(predicate: P) -> Box<Fn(&'a I) -> MatchResult>
491where T: Debug + 'a,
492      &'a I: IntoIterator<Item=&'a T> + 'a,
493      P: Fn(&'a T) -> bool + 'static {
494    Box::new(move |elements: &'a I| {
495        let builder = MatchResultBuilder::for_("all_elements_satisfy");
496        let nonsatisfying_elements: Vec<_> = elements.into_iter().filter(|e| !predicate(e)).collect();
497        if !nonsatisfying_elements.is_empty() {
498            builder.failed_because(
499                &format!("the following elements do not satisfy the predicate: {:?}", nonsatisfying_elements)
500            )
501        } else {
502            builder.matched()
503        }
504    })
505}
506
507/// Matches if at least one element in the asserted collection satisfy the given `predicate`.
508///
509/// An empty collection never satisfies this matcher as no element satisfies the predicate.
510///
511/// #Examples
512/// ```rust
513/// # #[macro_use] extern crate galvanic_assert;
514/// use galvanic_assert::matchers::collection::*;
515/// # fn main() {
516/// assert_that!(&vec![1,2,3,4,5], some_elements_satisfy(|&a| 2 <= a && a < 5));
517/// # }
518pub fn some_elements_satisfy<'a,T,I,P>(predicate: P) -> Box<Fn(&'a I) -> MatchResult>
519where T: Debug + 'a,
520      &'a I: IntoIterator<Item=&'a T> + 'a,
521      P: Fn(&T) -> bool + 'static {
522    Box::new(move |elements: &'a I| {
523        let builder = MatchResultBuilder::for_("some_elements_satisfy");
524        if !elements.into_iter().any(|ref e| predicate(e)) {
525            builder.failed_because("no elements satisfy the predicate")
526        } else {
527            builder.matched()
528        }
529    })
530}
531
532/// Matches if the map-like collection contains the given key/value pair.
533///
534/// The `Matcher` tests for this by converting the map-like data structure
535/// into a key/value pair iterator.
536///
537/// The alternative would be to use the Index trait though experiments showed
538/// that this would not be composable with `all_of!` or `any_of!`.
539pub struct HasEntry<K,V> {
540    key: K,
541    value: V
542}
543
544/// Matches if the map-like collection contains the given key/value pair.
545///
546/// The `Matcher` tests for this by converting the map-like data structure
547/// into a key/value pair iterator.
548///
549/// The alternative would be to use the Index trait though experiments showed
550/// that this would not be composable with `all_of!` or `any_of!`.
551///
552/// #Examples
553/// ```rust
554/// # #[macro_use] extern crate galvanic_assert;
555/// use galvanic_assert::matchers::collection::*;
556/// # fn main() {
557/// let mut map = std::collections::HashMap::<i32,i32>::new();
558/// map.insert(0, 2);
559/// map.insert(1, 2);
560/// map.insert(2, 5);
561/// map.insert(3, 3);
562/// map.insert(4, 3);
563///
564/// assert_that!(&map, has_entry(1, 2));
565/// # }
566pub fn has_entry<'a,K:'a,V:'a,M:'a>(key: K, value: V) -> Box<Matcher<'a,M> + 'a>
567where &'a M: IntoIterator<Item=(&'a K,&'a V)> + 'a,
568      HasEntry<K,V>: Matcher<'a,M> {
569    Box::new(HasEntry {
570        key: key,
571        value: value
572    })
573}
574
575impl<'a,K,V,M> Matcher<'a,M> for HasEntry<K,V>
576where V: PartialEq + Debug + 'a,
577      K: PartialEq + Debug + 'a,
578      &'a M: IntoIterator<Item=(&'a K,&'a V)> + 'a {
579
580    fn check(&self, map: &'a M) -> MatchResult {
581        let builder = MatchResultBuilder::for_("has_entry");
582        let mut same_keys = Vec::new();
583        let mut same_values = Vec::new();
584        for (key, value) in map.into_iter() {
585            if key == &self.key && value == &self.value {
586                return builder.matched()
587            }
588            if key == &self.key {
589                same_keys.push(value);
590            }
591            if value == &self.value {
592                same_values.push(key);
593            }
594        }
595
596        builder.failed_because(&format!(
597            "Entry ({:?}, {:?}) not found.\n\tEntries with same key: {:?}\n\tEntries with same value: {:?}",
598            &self.key, &self.value,
599            same_keys, same_values
600        ))
601    }
602}
603
604/// Matches if the map-like collection contains the given key.
605///
606/// The `Matcher` tests for this by converting the map-like data structure
607/// into a key/value pair iterator.
608///
609/// The alternative would be to use the Index trait though experiments showed
610/// that this would not be composable with `all_of!` or `any_of!`.
611pub struct HasKey<K> {
612    key: K
613}
614
615/// Matches if the map-like collection contains the given key.
616///
617/// The `Matcher` tests for this by converting the map-like data structure
618/// into a key/value pair iterator.
619///
620/// The alternative would be to use the Index trait though experiments showed
621/// that this would not be composable with `all_of!` or `any_of!`.
622///
623/// #Examples
624/// ```rust
625/// # #[macro_use] extern crate galvanic_assert;
626/// use galvanic_assert::matchers::collection::*;
627/// # fn main() {
628/// let mut map = std::collections::HashMap::<i32,i32>::new();
629/// map.insert(0, 2);
630/// map.insert(1, 2);
631/// map.insert(2, 5);
632/// map.insert(3, 3);
633/// map.insert(4, 3);
634///
635/// assert_that!(&map, has_key(2));
636/// # }
637pub fn has_key<'a,K:'a,V:'a,M:'a>(key: K) -> Box<Matcher<'a,M> + 'a>
638where &'a M: IntoIterator<Item=(&'a K,&'a V)> + 'a,
639      HasKey<K>: Matcher<'a,M> {
640    Box::new(HasKey {
641        key: key
642    })
643}
644
645impl<'a,K,V,M> Matcher<'a,M> for HasKey<K>
646where V: PartialEq + Debug + 'a,
647      K: PartialEq + Debug + 'a,
648      &'a M: IntoIterator<Item=(&'a K,&'a V)> + 'a {
649
650    fn check(&self, map: &'a M) -> MatchResult {
651        let builder = MatchResultBuilder::for_("has_key");
652        for (key, _) in map.into_iter() {
653            if key == &self.key {
654                return builder.matched();
655            }
656        }
657
658        builder.failed_because(&format!("No entrywith key {:?} found", &self.key))
659    }
660}
661
662
663/// Matches if the map-like collection contains the given value.
664///
665/// The `Matcher` tests for this by converting the map-like data structure
666/// into a key/value pair iterator.
667pub struct HasValue<V> {
668    value: V
669}
670
671/// Matches if the map-like collection contains the given value.
672///
673/// The `Matcher` tests for this by converting the map-like data structure
674/// into a key/value pair iterator.
675///
676/// #Examples
677/// ```rust
678/// # #[macro_use] extern crate galvanic_assert;
679/// use galvanic_assert::matchers::collection::*;
680/// # fn main() {
681/// let mut map = std::collections::HashMap::<i32,i32>::new();
682/// map.insert(0, 2);
683/// map.insert(1, 2);
684/// map.insert(2, 5);
685/// map.insert(3, 3);
686/// map.insert(4, 3);
687///
688/// assert_that!(&map, has_value(3));
689/// # }
690pub fn has_value<'a,K:'a,V:'a,M:'a>(key: K) -> Box<Matcher<'a,M> + 'a>
691where &'a M: IntoIterator<Item=(&'a K,&'a V)> + 'a,
692      HasKey<K>: Matcher<'a,M> {
693    Box::new(HasKey {
694        key: key
695    })
696}
697
698impl<'a,K,V,M> Matcher<'a,M> for HasValue<V>
699where V: PartialEq + Debug + 'a,
700      K: PartialEq + Debug + 'a,
701      &'a M: IntoIterator<Item=(&'a K,&'a V)> + 'a {
702
703    fn check(&self, map: &'a M) -> MatchResult {
704        let builder = MatchResultBuilder::for_("has_value");
705        for (_, value) in map.into_iter() {
706            if value == &self.value {
707                return builder.matched();
708            }
709        }
710
711        builder.failed_because(&format!("No entry with value {:?} found", &self.value))
712    }
713}