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}