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 {
485 let mut actual_match = vec![None; self.0.len()];
486 let mut expected_match: Vec<Option<usize>> = vec![None; self.1];
487 for actual_idx in 0..self.0.len() {
501 assert!(actual_match[actual_idx].is_none());
502 let mut seen = vec![false; self.1];
503 self.try_augment(actual_idx, &mut seen, &mut actual_match, &mut expected_match);
504 }
505 BestMatch(actual_match, self.1)
506 }
507
508 fn try_augment(
528 &self,
529 actual_idx: usize,
530 seen: &mut [bool],
531 actual_match: &mut [Option<usize>],
532 expected_match: &mut [Option<usize>],
533 ) -> bool {
534 for expected_idx in 0..expected_match.len() {
535 if seen[expected_idx] {
536 continue;
537 }
538 if self.0[actual_idx][expected_idx].is_no_match() {
539 continue;
540 }
541 seen[expected_idx] = true;
543 if expected_match[expected_idx].is_none()
556 || self.try_augment(
557 expected_match[expected_idx].unwrap(),
558 seen,
559 actual_match,
560 expected_match,
561 )
562 {
563 expected_match[expected_idx] = Some(actual_idx);
569 actual_match[actual_idx] = Some(expected_idx);
570 return true;
571 }
572 }
573 false
574 }
575 }
576
577 struct UnmatchableElements {
583 unmatchable_actual: Vec<bool>,
584 unmatchable_expected: Vec<bool>,
585 }
586
587 impl UnmatchableElements {
588 fn has_unmatchable_elements(&self) -> bool {
589 self.unmatchable_actual.iter().any(|b| *b)
590 || self.unmatchable_expected.iter().any(|b| *b)
591 }
592
593 fn get_explanation(&self) -> Option<Description> {
594 let unmatchable_actual = self.unmatchable_actual();
595 let actual_idx = unmatchable_actual
596 .iter()
597 .map(|idx| format!("#{}", idx))
598 .collect::<Vec<_>>()
599 .join(", ");
600 let unmatchable_expected = self.unmatchable_expected();
601 let expected_idx = unmatchable_expected
602 .iter()
603 .map(|idx| format!("#{}", idx))
604 .collect::<Vec<_>>()
605 .join(", ");
606 match (unmatchable_actual.len(), unmatchable_expected.len()) {
607 (0, 0) => None,
608 (1, 0) => {
609 Some(format!("whose element {actual_idx} does not match any expected elements").into())
610 }
611 (_, 0) => {
612 Some(format!("whose elements {actual_idx} do not match any expected elements",).into())
613 }
614 (0, 1) => Some(format!(
615 "which has no element matching the expected element {expected_idx}"
616 ).into()),
617 (0, _) => Some(format!(
618 "which has no elements matching the expected elements {expected_idx}"
619 ).into()),
620 (1, 1) => Some(format!(
621 "whose element {actual_idx} does not match any expected elements and no elements match the expected element {expected_idx}"
622 ).into()),
623 (_, 1) => Some(format!(
624 "whose elements {actual_idx} do not match any expected elements and no elements match the expected element {expected_idx}"
625 ).into()),
626 (1, _) => Some(format!(
627 "whose element {actual_idx} does not match any expected elements and no elements match the expected elements {expected_idx}"
628 ).into()),
629 (_, _) => Some(format!(
630 "whose elements {actual_idx} do not match any expected elements and no elements match the expected elements {expected_idx}"
631 ).into()),
632 }
633 }
634
635 fn unmatchable_actual(&self) -> Vec<usize> {
636 self.unmatchable_actual
637 .iter()
638 .enumerate()
639 .filter_map(|(idx, b)| if *b { Some(idx) } else { None })
640 .collect()
641 }
642
643 fn unmatchable_expected(&self) -> Vec<usize> {
644 self.unmatchable_expected
645 .iter()
646 .enumerate()
647 .filter_map(|(idx, b)| if *b { Some(idx) } else { None })
648 .collect()
649 }
650 }
651
652 struct BestMatch(Vec<Option<usize>>, usize);
660
661 impl BestMatch {
662 fn is_full_match(&self) -> bool {
663 self.0.iter().all(|o| o.is_some())
664 }
665
666 fn is_subset_match(&self) -> bool {
667 self.is_full_match()
668 }
669
670 fn is_superset_match(&self) -> bool {
671 self.get_unmatched_expected().is_empty()
672 }
673
674 fn get_matches(&self) -> impl Iterator<Item = (usize, usize)> + '_ {
675 self.0.iter().enumerate().filter_map(|(actual_idx, maybe_expected_idx)| {
676 maybe_expected_idx.map(|expected_idx| (actual_idx, expected_idx))
677 })
678 }
679
680 fn get_unmatched_actual(&self) -> impl Iterator<Item = usize> + '_ {
681 self.0
682 .iter()
683 .enumerate()
684 .filter(|&(_, o)| o.is_none())
685 .map(|(actual_idx, _)| actual_idx)
686 }
687
688 fn get_unmatched_expected(&self) -> Vec<usize> {
689 let matched_expected: BTreeSet<_> = self.0.iter().flatten().collect();
690 (0..self.1).filter(|expected_idx| !matched_expected.contains(expected_idx)).collect()
691 }
692
693 fn get_explanation<'matchers, T: Debug, ItemT: Borrow<T>>(
694 &self,
695 actual_items: impl Iterator<Item = ItemT>,
696 expected: &[Box<dyn Matcher<T> + 'matchers>],
697 requirements: Requirements,
698 ) -> Option<Description> {
699 let actual: Vec<ItemT> = actual_items.collect();
700 if self.is_full_match() {
701 return None;
702 }
703
704 let matches = self.get_matches().map(|(actual_idx, expected_idx)| {
705 format!(
706 "Actual element {:?} at index {actual_idx} matched expected element `{}` at index {expected_idx}.",
707 actual[actual_idx].borrow(),
708 expected[expected_idx].describe(MatcherResult::Match),
709 )
710 });
711
712 let unmatched_actual = self.get_unmatched_actual().map(|actual_idx| {
713 format!(
714 "Actual element {:#?} at index {actual_idx} did not match any remaining expected element.",
715 actual[actual_idx].borrow()
716 )
717 });
718
719 let unmatched_expected =
720 self.get_unmatched_expected().into_iter().map(|expected_idx| {
721 format!(
722 "Expected element `{}` at index {expected_idx} did not match any remaining actual element.",
723 expected[expected_idx].describe(MatcherResult::Match)
724 )
725 });
726
727 let best_match = matches
728 .chain(unmatched_actual)
729 .chain(unmatched_expected)
730 .collect::<Description>()
731 .indent();
732 Some(
733 format!(
734 "which does not have a {requirements} match with the expected elements. The best match found was:\n{best_match}"
735 )
736 .into(),
737 )
738 }
739
740 fn get_explanation_for_map<
741 'matchers,
742 KeyT: Debug,
743 ValueT: Debug,
744 ItemT: PairBorrow<KeyT, ValueT>,
745 >(
746 &self,
747 actual_items: impl Iterator<Item = ItemT>,
748 expected: &[KeyValueMatcher<'matchers, KeyT, ValueT>],
749 requirements: Requirements,
750 ) -> Option<Description> {
751 let actual: Vec<ItemT> = actual_items.collect();
752 if self.is_full_match() {
753 return None;
754 }
755
756 let matches = self.get_matches().map(|(actual_idx, expected_idx)| {
757 format!(
758 "Actual element {:?} => {:?} at index {actual_idx} matched expected element `{}` => `{}` at index {expected_idx}.",
759 actual[actual_idx].borrow_key(),
760 actual[actual_idx].borrow_value(),
761 expected[expected_idx].0.describe(MatcherResult::Match),
762 expected[expected_idx].1.describe(MatcherResult::Match),
763 )
764 });
765
766 let unmatched_actual = self.get_unmatched_actual().map(|actual_idx| {
767 format!(
768 "Actual element {:#?} => {:#?} at index {actual_idx} did not match any remaining expected element.",
769 actual[actual_idx].borrow_key(),
770 actual[actual_idx].borrow_value(),
771 )
772 });
773
774 let unmatched_expected =
775 self.get_unmatched_expected().into_iter().map(|expected_idx| {
776 format!(
777 "Expected element `{}` => `{}` at index {expected_idx} did not match any remaining actual element.",
778 expected[expected_idx].0.describe(MatcherResult::Match),
779 expected[expected_idx].1.describe(MatcherResult::Match),
780 )
781 });
782
783 let best_match = matches
784 .chain(unmatched_actual)
785 .chain(unmatched_expected)
786 .collect::<Description>()
787 .indent();
788 Some(
789 format!(
790 "which does not have a {requirements} match with the expected elements. The best match found was:\n{best_match}"
791 )
792 .into(),
793 )
794 }
795 }
796}
797
798#[cfg(all(test, feature = "std"))]
799mod tests {
800 use super::__internal::MapContainsMatcher;
801 use crate::matcher::Matcher;
802 use crate::matchers::containers::RefItems;
803 use crate::prelude::*;
804 use indoc::indoc;
805 use std::collections::HashMap;
806
807 #[test]
808 fn has_correct_description_for_map() -> TestResult<()> {
809 let matchers = ((eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three")));
816 let result = verify_that!(
817 HashMap::from([(1, "one")]),
818 contains_exactly![
819 matchers.0.0 => matchers.0.1,
820 matchers.1.0 => matchers.1.1,
821 matchers.2.0 => matchers.2.1
822 ]
823 );
824 verify_that!(
825 result,
826 err(displays_as(contains_substring(indoc!(
827 "
828 contains elements matching in any order:
829 is equal to 2 => is equal to \"Two\"
830 is equal to 1 => is equal to \"One\"
831 is equal to 3 => is equal to \"Three\""
832 ))))
833 )
834 }
835
836 #[cfg(feature = "regex")]
837 #[test]
838 fn contains_exactly_description_no_full_match_with_map() -> TestResult<()> {
839 let matchers = ((anything(), eq(1)), (anything(), eq(2)), (anything(), eq(2)));
846 let matcher: MapContainsMatcher<HashMap<u32, u32>, _, _, RefItems, 3> = contains_exactly![
847 matchers.0.0 => matchers.0.1,
848 matchers.1.0 => matchers.1.1,
849 matchers.2.0 => matchers.2.1,
850 ];
851 let value: HashMap<u32, u32> = HashMap::from_iter([(0, 1), (1, 1), (2, 2)]);
852 verify_that!(
853 matcher.explain_match(&value),
854 displays_as(
855 contains_regex(
856 "Actual element 2 => 2 at index [0-2] matched expected element `is anything` => `is equal to 2` at index [0-2]."
857 )
858 )
859 .and(
860 displays_as(
861 contains_regex(
862 "Actual element [0-1] => [0-1] at index [0-2] did not match any remaining expected element."
863 )
864 )
865 )
866 .and(
867 displays_as(
868 contains_substring(
869 "Expected element `is anything` => `is equal to 2` at index 2 did not match any remaining actual element."
870 )
871 )
872 )
873 )
874 }
875}