1#![doc(hidden)]
19
20#[doc(hidden)]
24pub mod __internal {
25 use crate::{
26 description::Description,
27 matcher::{Describable, Matcher, MatcherResult},
28 matcher_support::count_elements::count_elements,
29 matchers::__internal::ContainerContainsOrderedMatcher,
30 matchers::containers::{
31 OwnedItems, RefItems,
32 container_contains::{PairBorrow, Requirements},
33 },
34 };
35 use alloc::{boxed::Box, collections::BTreeSet, vec::Vec};
36 use core::{borrow::Borrow, fmt::Debug, marker::PhantomData};
37
38 pub struct ContainerContainsUnorderedMatcher<'matchers, ContainerT: ?Sized, T: Debug, ModeT> {
44 elements: Vec<Box<dyn Matcher<T> + 'matchers>>,
45 requirements: Requirements,
46 _phantom: PhantomData<(*const ContainerT, ModeT)>,
47 }
48
49 impl<'matchers, ContainerT: ?Sized, T: Debug, ModeT>
50 ContainerContainsUnorderedMatcher<'matchers, ContainerT, T, ModeT>
51 {
52 #[doc(hidden)]
59 pub fn new(
60 elements: Vec<Box<dyn Matcher<T> + 'matchers>>,
61 requirements: Requirements,
62 ) -> Self {
63 Self { elements, requirements, _phantom: PhantomData }
64 }
65
66 pub fn in_order(self) -> ContainerContainsOrderedMatcher<'matchers, ContainerT, T, ModeT> {
69 ContainerContainsOrderedMatcher::new(self.elements, self.requirements)
70 }
71
72 fn matches_with_iter<ItemT: Borrow<T>>(
73 &self,
74 n: usize,
75 actual: impl Iterator<Item = ItemT>,
76 ) -> MatcherResult {
77 let match_matrix = MatchMatrix::generate(actual, n, &self.elements);
78 match_matrix.is_match_for(self.requirements).into()
79 }
80
81 fn explain_match_with_iters<ItemT1: Borrow<T>, ItemT2: Borrow<T>>(
89 &self,
90 size_mismatch: Option<Description>,
91 n: usize,
92 iter_for_generate: impl Iterator<Item = ItemT1>,
93 iter_for_explain: impl Iterator<Item = ItemT2>,
94 ) -> Description {
95 if let Some(explanation) = size_mismatch {
96 return explanation;
97 }
98 let match_matrix = MatchMatrix::generate(iter_for_generate, n, &self.elements);
99 if let Some(unmatchable) = match_matrix.explain_unmatchable(self.requirements) {
100 return unmatchable;
101 }
102 let best_match = match_matrix.find_best_match();
103 best_match
104 .get_explanation(iter_for_explain, &self.elements, self.requirements)
105 .unwrap_or("whose elements all match".into())
106 }
107 }
108
109 impl<'matchers, T: Debug, ContainerT: Debug + ?Sized> Matcher<ContainerT>
110 for ContainerContainsUnorderedMatcher<'matchers, ContainerT, T, RefItems>
111 where
112 for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
113 {
114 fn matches(&self, actual: &ContainerT) -> MatcherResult {
115 self.matches_with_iter(count_elements(actual), actual.into_iter())
116 }
117
118 fn explain_match(&self, actual: &ContainerT) -> Description {
119 self.explain_match_with_iters(
120 self.requirements.explain_size_mismatch(actual, self.elements.len()),
121 count_elements(actual),
122 actual.into_iter(),
123 actual.into_iter(),
124 )
125 }
126 }
127
128 impl<'matchers, T: Debug, ContainerT: Debug + ?Sized> Matcher<ContainerT>
129 for ContainerContainsUnorderedMatcher<'matchers, ContainerT, T, OwnedItems>
130 where
131 for<'b> &'b ContainerT: IntoIterator<Item = T>,
132 {
133 fn matches(&self, actual: &ContainerT) -> MatcherResult {
134 self.matches_with_iter(count_elements(actual), actual.into_iter())
135 }
136
137 fn explain_match(&self, actual: &ContainerT) -> Description {
138 self.explain_match_with_iters(
139 self.requirements.explain_size_mismatch(actual, self.elements.len()),
140 count_elements(actual),
141 actual.into_iter(),
142 actual.into_iter(),
143 )
144 }
145 }
146
147 impl<'matchers, T: Debug, ContainerT: ?Sized, ModeT> Describable
148 for ContainerContainsUnorderedMatcher<'matchers, ContainerT, T, ModeT>
149 {
150 fn describe(&self, matcher_result: MatcherResult) -> Description {
151 format!(
152 "{} elements matching in any order:\n{}",
153 if matcher_result.into() { "contains" } else { "doesn't contain" },
154 self.elements
155 .iter()
156 .map(|matcher| matcher.describe(MatcherResult::Match))
157 .collect::<Description>()
158 .enumerate()
159 .indent()
160 )
161 .into()
162 }
163 }
164
165 type KeyValueMatcher<'a, KeyT, ValueT> =
166 (Box<dyn Matcher<KeyT> + 'a>, Box<dyn Matcher<ValueT> + 'a>);
167
168 #[doc(hidden)]
173 pub struct MapContainsMatcher<'matchers, ContainerT, KeyT, ValueT, ModeT, const N: usize>
174 where
175 ContainerT: ?Sized,
176 KeyT: Debug,
177 ValueT: Debug,
178 {
179 elements: [KeyValueMatcher<'matchers, KeyT, ValueT>; N],
180 requirements: Requirements,
181 phantom: PhantomData<(ModeT, ContainerT)>,
182 }
183
184 impl<'matchers, ContainerT: ?Sized, KeyT: Debug, ValueT: Debug, ModeT, const N: usize>
185 MapContainsMatcher<'matchers, ContainerT, KeyT, ValueT, ModeT, N>
186 {
187 pub fn new(
188 elements: [KeyValueMatcher<'matchers, KeyT, ValueT>; N],
189 requirements: Requirements,
190 ) -> Self {
191 Self { elements, requirements, phantom: PhantomData }
192 }
193
194 fn matches_with_iter<ItemT: PairBorrow<KeyT, ValueT>>(
195 &self,
196 n: usize,
197 actual: impl Iterator<Item = ItemT>,
198 ) -> MatcherResult {
199 let match_matrix = MatchMatrix::generate_for_map(actual, n, &self.elements);
200 match_matrix.is_match_for(self.requirements).into()
201 }
202
203 fn explain_match_with_iters<
204 ItemT1: PairBorrow<KeyT, ValueT>,
205 ItemT2: PairBorrow<KeyT, ValueT>,
206 >(
207 &self,
208 size_mismatch: Option<Description>,
209 n: usize,
210 iter_for_generate: impl Iterator<Item = ItemT1>,
211 iter_for_explain: impl Iterator<Item = ItemT2>,
212 ) -> Description {
213 if let Some(explanation) = size_mismatch {
214 return explanation;
215 }
216 let match_matrix = MatchMatrix::generate_for_map(iter_for_generate, n, &self.elements);
217 if let Some(unmatchable) = match_matrix.explain_unmatchable(self.requirements) {
218 return unmatchable;
219 }
220 let best_match = match_matrix.find_best_match();
221 best_match
222 .get_explanation_for_map(iter_for_explain, &self.elements, self.requirements)
223 .unwrap_or("whose elements all match".into())
224 }
225 }
226
227 impl<'matchers, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized, const N: usize>
228 Matcher<ContainerT> for MapContainsMatcher<'matchers, ContainerT, KeyT, ValueT, RefItems, N>
229 where
230 for<'b> &'b ContainerT: IntoIterator<Item = (&'b KeyT, &'b ValueT)>,
231 {
232 fn matches(&self, actual: &ContainerT) -> MatcherResult {
233 self.matches_with_iter(count_elements(actual), actual.into_iter())
234 }
235
236 fn explain_match(&self, actual: &ContainerT) -> Description {
237 self.explain_match_with_iters(
238 self.requirements.explain_size_mismatch(actual, N),
239 count_elements(actual),
240 actual.into_iter(),
241 actual.into_iter(),
242 )
243 }
244 }
245
246 impl<'matchers, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized, const N: usize>
247 Matcher<ContainerT>
248 for MapContainsMatcher<'matchers, ContainerT, KeyT, ValueT, OwnedItems, N>
249 where
250 for<'b> &'b ContainerT: IntoIterator<Item = (KeyT, ValueT)>,
251 {
252 fn matches(&self, actual: &ContainerT) -> MatcherResult {
253 self.matches_with_iter(count_elements(actual), actual.into_iter())
254 }
255
256 fn explain_match(&self, actual: &ContainerT) -> Description {
257 self.explain_match_with_iters(
258 self.requirements.explain_size_mismatch(actual, N),
259 count_elements(actual),
260 actual.into_iter(),
261 actual.into_iter(),
262 )
263 }
264 }
265
266 impl<'matchers, KeyT: Debug, ValueT: Debug, ContainerT: ?Sized, ModeT, const N: usize>
267 Describable for MapContainsMatcher<'matchers, ContainerT, KeyT, ValueT, ModeT, N>
268 {
269 fn describe(&self, matcher_result: MatcherResult) -> Description {
270 format!(
271 "{} elements matching in any order:\n{}",
272 if matcher_result.into() { "contains" } else { "doesn't contain" },
273 self.elements
274 .iter()
275 .map(|(key_matcher, value_matcher)| format!(
276 "{} => {}",
277 key_matcher.describe(MatcherResult::Match),
278 value_matcher.describe(MatcherResult::Match)
279 ))
280 .collect::<Description>()
281 .indent()
282 )
283 .into()
284 }
285 }
286
287 pub(crate) struct MatchMatrix(Vec<Vec<MatcherResult>>, usize);
289
290 impl MatchMatrix {
291 fn generate<'matchers, ActualT: Debug + ?Sized + 'matchers, ItemT: Borrow<ActualT>>(
292 actual_items: impl Iterator<Item = ItemT>,
293 n_actual: usize,
294 expected: &[Box<dyn Matcher<ActualT> + 'matchers>],
295 ) -> Self {
296 let mut matrix = MatchMatrix(
297 vec![vec![MatcherResult::NoMatch; expected.len()]; n_actual],
298 expected.len(),
299 );
300 for (actual_idx, actual_item) in actual_items.enumerate() {
301 for (expected_idx, expected) in expected.iter().enumerate() {
302 matrix.0[actual_idx][expected_idx] = expected.matches(actual_item.borrow());
303 }
304 }
305 matrix
306 }
307
308 pub(crate) fn generate_for_fixed_matcher<
309 'matchers,
310 MatcherT: Matcher<ActualT> + 'matchers,
311 ActualT: Debug + ?Sized + 'matchers,
312 ItemT: Borrow<ActualT>,
313 >(
314 actual_items: impl Iterator<Item = ItemT>,
315 n_actual: usize,
316 expected: &[MatcherT],
317 ) -> Self {
318 let mut matrix = MatchMatrix(
319 vec![vec![MatcherResult::NoMatch; expected.len()]; n_actual],
320 expected.len(),
321 );
322 for (actual_idx, actual_item) in actual_items.enumerate() {
323 for (expected_idx, expected) in expected.iter().enumerate() {
324 matrix.0[actual_idx][expected_idx] = expected.matches(actual_item.borrow());
325 }
326 }
327 matrix
328 }
329
330 fn generate_for_map<
331 'matchers,
332 KeyT: Debug,
333 ValueT: Debug,
334 ItemT: PairBorrow<KeyT, ValueT>,
335 >(
336 actual_items: impl Iterator<Item = ItemT>,
337 n_actual: usize,
338 expected: &[KeyValueMatcher<'matchers, KeyT, ValueT>],
339 ) -> Self {
340 let mut matrix = MatchMatrix(
341 vec![vec![MatcherResult::NoMatch; expected.len()]; n_actual],
342 expected.len(),
343 );
344 for (actual_idx, actual_item) in actual_items.enumerate() {
345 for (expected_idx, (expected_key, expected_value)) in expected.iter().enumerate() {
346 matrix.0[actual_idx][expected_idx] =
347 (expected_key.matches(actual_item.borrow_key()).into()
348 && expected_value.matches(actual_item.borrow_value()).into())
349 .into();
350 }
351 }
352 matrix
353 }
354
355 pub(crate) fn is_match_for(&self, requirements: Requirements) -> bool {
356 match requirements {
357 Requirements::PerfectMatch => {
358 !self.find_unmatchable_elements().has_unmatchable_elements()
359 && self.find_best_match().is_full_match()
360 }
361 Requirements::Superset => {
362 !self.find_unmatched_expected().has_unmatchable_elements()
363 && self.find_best_match().is_superset_match()
364 }
365 Requirements::Subset => {
366 !self.find_unmatched_actual().has_unmatchable_elements()
367 && self.find_best_match().is_subset_match()
368 }
369 }
370 }
371
372 fn explain_unmatchable(&self, requirements: Requirements) -> Option<Description> {
373 let unmatchable_elements = match requirements {
374 Requirements::PerfectMatch => self.find_unmatchable_elements(),
375 Requirements::Superset => self.find_unmatched_expected(),
376 Requirements::Subset => self.find_unmatched_actual(),
377 };
378 unmatchable_elements.get_explanation()
379 }
380
381 fn find_unmatchable_elements(&self) -> UnmatchableElements {
386 let unmatchable_actual =
387 self.0.iter().map(|row| row.iter().all(|&e| e.is_no_match())).collect();
388 let mut unmatchable_expected = vec![false; self.1];
389 for (col_idx, expected) in unmatchable_expected.iter_mut().enumerate() {
390 *expected = self.0.iter().map(|row| row[col_idx]).all(|e| e.is_no_match());
391 }
392 UnmatchableElements { unmatchable_actual, unmatchable_expected }
393 }
394
395 fn find_unmatched_expected(&self) -> UnmatchableElements {
396 let mut unmatchable_expected = vec![false; self.1];
397 for (col_idx, expected) in unmatchable_expected.iter_mut().enumerate() {
398 *expected = self.0.iter().map(|row| row[col_idx]).all(|e| e.is_no_match());
399 }
400 UnmatchableElements {
401 unmatchable_actual: vec![false; self.0.len()],
402 unmatchable_expected,
403 }
404 }
405
406 fn find_unmatched_actual(&self) -> UnmatchableElements {
407 let unmatchable_actual =
408 self.0.iter().map(|row| row.iter().all(|e| e.is_no_match())).collect();
409 UnmatchableElements {
410 unmatchable_actual,
411 unmatchable_expected: vec![false; self.0.len()],
412 }
413 }
414
415 fn find_best_match(&self) -> BestMatch {
480 let mut actual_match = vec![None; self.0.len()];
481 let mut expected_match: Vec<Option<usize>> = vec![None; self.1];
482 for actual_idx in 0..self.0.len() {
496 assert!(actual_match[actual_idx].is_none());
497 let mut seen = vec![false; self.1];
498 self.try_augment(actual_idx, &mut seen, &mut actual_match, &mut expected_match);
499 }
500 BestMatch(actual_match, self.1)
501 }
502
503 fn try_augment(
522 &self,
523 actual_idx: usize,
524 seen: &mut [bool],
525 actual_match: &mut [Option<usize>],
526 expected_match: &mut [Option<usize>],
527 ) -> bool {
528 for expected_idx in 0..expected_match.len() {
529 if seen[expected_idx] {
530 continue;
531 }
532 if self.0[actual_idx][expected_idx].is_no_match() {
533 continue;
534 }
535 seen[expected_idx] = true;
537 if expected_match[expected_idx].is_none()
549 || self.try_augment(
550 expected_match[expected_idx].unwrap(),
551 seen,
552 actual_match,
553 expected_match,
554 )
555 {
556 expected_match[expected_idx] = Some(actual_idx);
561 actual_match[actual_idx] = Some(expected_idx);
562 return true;
563 }
564 }
565 false
566 }
567 }
568
569 struct UnmatchableElements {
575 unmatchable_actual: Vec<bool>,
576 unmatchable_expected: Vec<bool>,
577 }
578
579 impl UnmatchableElements {
580 fn has_unmatchable_elements(&self) -> bool {
581 self.unmatchable_actual.iter().any(|b| *b)
582 || self.unmatchable_expected.iter().any(|b| *b)
583 }
584
585 fn get_explanation(&self) -> Option<Description> {
586 let unmatchable_actual = self.unmatchable_actual();
587 let actual_idx = unmatchable_actual
588 .iter()
589 .map(|idx| format!("#{}", idx))
590 .collect::<Vec<_>>()
591 .join(", ");
592 let unmatchable_expected = self.unmatchable_expected();
593 let expected_idx = unmatchable_expected
594 .iter()
595 .map(|idx| format!("#{}", idx))
596 .collect::<Vec<_>>()
597 .join(", ");
598 match (unmatchable_actual.len(), unmatchable_expected.len()) {
599 (0, 0) => None,
600 (1, 0) => {
601 Some(format!("whose element {actual_idx} does not match any expected elements").into())
602 }
603 (_, 0) => {
604 Some(format!("whose elements {actual_idx} do not match any expected elements",).into())
605 }
606 (0, 1) => Some(format!(
607 "which has no element matching the expected element {expected_idx}"
608 ).into()),
609 (0, _) => Some(format!(
610 "which has no elements matching the expected elements {expected_idx}"
611 ).into()),
612 (1, 1) => Some(format!(
613 "whose element {actual_idx} does not match any expected elements and no elements match the expected element {expected_idx}"
614 ).into()),
615 (_, 1) => Some(format!(
616 "whose elements {actual_idx} do not match any expected elements and no elements match the expected element {expected_idx}"
617 ).into()),
618 (1, _) => Some(format!(
619 "whose element {actual_idx} does not match any expected elements and no elements match the expected elements {expected_idx}"
620 ).into()),
621 (_, _) => Some(format!(
622 "whose elements {actual_idx} do not match any expected elements and no elements match the expected elements {expected_idx}"
623 ).into()),
624 }
625 }
626
627 fn unmatchable_actual(&self) -> Vec<usize> {
628 self.unmatchable_actual
629 .iter()
630 .enumerate()
631 .filter_map(|(idx, b)| if *b { Some(idx) } else { None })
632 .collect()
633 }
634
635 fn unmatchable_expected(&self) -> Vec<usize> {
636 self.unmatchable_expected
637 .iter()
638 .enumerate()
639 .filter_map(|(idx, b)| if *b { Some(idx) } else { None })
640 .collect()
641 }
642 }
643
644 struct BestMatch(Vec<Option<usize>>, usize);
652
653 impl BestMatch {
654 fn is_full_match(&self) -> bool {
655 self.0.iter().all(|o| o.is_some())
656 }
657
658 fn is_subset_match(&self) -> bool {
659 self.is_full_match()
660 }
661
662 fn is_superset_match(&self) -> bool {
663 self.get_unmatched_expected().is_empty()
664 }
665
666 fn get_matches(&self) -> impl Iterator<Item = (usize, usize)> + '_ {
667 self.0.iter().enumerate().filter_map(|(actual_idx, maybe_expected_idx)| {
668 maybe_expected_idx.map(|expected_idx| (actual_idx, expected_idx))
669 })
670 }
671
672 fn get_unmatched_actual(&self) -> impl Iterator<Item = usize> + '_ {
673 self.0
674 .iter()
675 .enumerate()
676 .filter(|&(_, o)| o.is_none())
677 .map(|(actual_idx, _)| actual_idx)
678 }
679
680 fn get_unmatched_expected(&self) -> Vec<usize> {
681 let matched_expected: BTreeSet<_> = self.0.iter().flatten().collect();
682 (0..self.1).filter(|expected_idx| !matched_expected.contains(expected_idx)).collect()
683 }
684
685 fn get_explanation<'matchers, T: Debug, ItemT: Borrow<T>>(
686 &self,
687 actual_items: impl Iterator<Item = ItemT>,
688 expected: &[Box<dyn Matcher<T> + 'matchers>],
689 requirements: Requirements,
690 ) -> Option<Description> {
691 let actual: Vec<ItemT> = actual_items.collect();
692 if self.is_full_match() {
693 return None;
694 }
695
696 let matches = self.get_matches().map(|(actual_idx, expected_idx)| {
697 format!(
698 "Actual element {:?} at index {actual_idx} matched expected element `{}` at index {expected_idx}.",
699 actual[actual_idx].borrow(),
700 expected[expected_idx].describe(MatcherResult::Match),
701 )
702 });
703
704 let unmatched_actual = self.get_unmatched_actual().map(|actual_idx| {
705 format!(
706 "Actual element {:#?} at index {actual_idx} did not match any remaining expected element.",
707 actual[actual_idx].borrow()
708 )
709 });
710
711 let unmatched_expected =
712 self.get_unmatched_expected().into_iter().map(|expected_idx| {
713 format!(
714 "Expected element `{}` at index {expected_idx} did not match any remaining actual element.",
715 expected[expected_idx].describe(MatcherResult::Match)
716 )
717 });
718
719 let best_match = matches
720 .chain(unmatched_actual)
721 .chain(unmatched_expected)
722 .collect::<Description>()
723 .indent();
724 Some(
725 format!(
726 "which does not have a {requirements} match with the expected elements. The best match found was:\n{best_match}"
727 )
728 .into(),
729 )
730 }
731
732 fn get_explanation_for_map<
733 'matchers,
734 KeyT: Debug,
735 ValueT: Debug,
736 ItemT: PairBorrow<KeyT, ValueT>,
737 >(
738 &self,
739 actual_items: impl Iterator<Item = ItemT>,
740 expected: &[KeyValueMatcher<'matchers, KeyT, ValueT>],
741 requirements: Requirements,
742 ) -> Option<Description> {
743 let actual: Vec<ItemT> = actual_items.collect();
744 if self.is_full_match() {
745 return None;
746 }
747
748 let matches = self.get_matches().map(|(actual_idx, expected_idx)| {
749 format!(
750 "Actual element {:?} => {:?} at index {actual_idx} matched expected element `{}` => `{}` at index {expected_idx}.",
751 actual[actual_idx].borrow_key(),
752 actual[actual_idx].borrow_value(),
753 expected[expected_idx].0.describe(MatcherResult::Match),
754 expected[expected_idx].1.describe(MatcherResult::Match),
755 )
756 });
757
758 let unmatched_actual = self.get_unmatched_actual().map(|actual_idx| {
759 format!(
760 "Actual element {:#?} => {:#?} at index {actual_idx} did not match any remaining expected element.",
761 actual[actual_idx].borrow_key(),
762 actual[actual_idx].borrow_value(),
763 )
764 });
765
766 let unmatched_expected =
767 self.get_unmatched_expected().into_iter().map(|expected_idx| {
768 format!(
769 "Expected element `{}` => `{}` at index {expected_idx} did not match any remaining actual element.",
770 expected[expected_idx].0.describe(MatcherResult::Match),
771 expected[expected_idx].1.describe(MatcherResult::Match),
772 )
773 });
774
775 let best_match = matches
776 .chain(unmatched_actual)
777 .chain(unmatched_expected)
778 .collect::<Description>()
779 .indent();
780 Some(
781 format!(
782 "which does not have a {requirements} match with the expected elements. The best match found was:\n{best_match}"
783 )
784 .into(),
785 )
786 }
787 }
788}
789
790#[cfg(all(test, feature = "std"))]
791mod tests {
792 use super::__internal::MapContainsMatcher;
793 use crate::matcher::Matcher;
794 use crate::matchers::containers::RefItems;
795 use crate::prelude::*;
796 use indoc::indoc;
797 use std::collections::HashMap;
798
799 #[test]
800 fn has_correct_description_for_map() -> TestResult<()> {
801 let matchers = ((eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three")));
807 let result = verify_that!(
808 HashMap::from([(1, "one")]),
809 contains_exactly![
810 matchers.0.0 => matchers.0.1,
811 matchers.1.0 => matchers.1.1,
812 matchers.2.0 => matchers.2.1
813 ]
814 );
815 verify_that!(
816 result,
817 err(displays_as(contains_substring(indoc!(
818 "
819 contains elements matching in any order:
820 is equal to 2 => is equal to \"Two\"
821 is equal to 1 => is equal to \"One\"
822 is equal to 3 => is equal to \"Three\""
823 ))))
824 )
825 }
826
827 #[cfg(feature = "regex")]
828 #[test]
829 fn contains_exactly_description_no_full_match_with_map() -> TestResult<()> {
830 let matchers = ((anything(), eq(1)), (anything(), eq(2)), (anything(), eq(2)));
836 let matcher: MapContainsMatcher<HashMap<u32, u32>, _, _, RefItems, 3> = contains_exactly![
837 matchers.0.0 => matchers.0.1,
838 matchers.1.0 => matchers.1.1,
839 matchers.2.0 => matchers.2.1,
840 ];
841 let value: HashMap<u32, u32> = HashMap::from_iter([(0, 1), (1, 1), (2, 2)]);
842 verify_that!(
843 matcher.explain_match(&value),
844 displays_as(
845 contains_regex(
846 "Actual element 2 => 2 at index [0-2] matched expected element `is anything` => `is equal to 2` at index [0-2]."
847 )
848 )
849 .and(
850 displays_as(
851 contains_regex(
852 "Actual element [0-1] => [0-1] at index [0-2] did not match any remaining expected element."
853 )
854 )
855 )
856 .and(
857 displays_as(
858 contains_substring(
859 "Expected element `is anything` => `is equal to 2` at index 2 did not match any remaining actual element."
860 )
861 )
862 )
863 )
864 }
865}