1 // Copyright 2022 Google LLC
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 // There are no visible documentation elements in this module; the declarative
16 // macro is documented in the matchers module.
17 #![doc(hidden)]
18 
19 /// Matches a container's elements to each matcher in order.
20 ///
21 /// This macro produces a matcher against a container. It takes as arguments a
22 /// sequence of matchers each of which should respectively match the
23 /// corresponding element of the actual value.
24 ///
25 /// ```
26 /// # use googletest::prelude::*;
27 /// verify_that!(vec![1, 2, 3], elements_are![eq(1), anything(), gt(0).and(lt(123))])
28 /// #    .unwrap();
29 /// ```
30 ///
31 /// The actual value must be a container such as a `Vec`, an array, or a
32 /// dereferenced slice. More precisely, a shared borrow of the actual value must
33 /// implement [`IntoIterator`].
34 ///
35 /// ```
36 /// # use googletest::prelude::*;
37 /// let vector = vec![1, 2, 3];
38 /// let slice = vector.as_slice();
39 /// verify_that!(*slice, elements_are![eq(1), anything(), gt(0).and(lt(123))])
40 /// #    .unwrap();
41 /// ```
42 ///
43 /// This can also be omitted in [`verify_that!`] macros and replaced with square
44 /// brackets.
45 ///
46 /// ```
47 /// # use googletest::prelude::*;
48 ///  verify_that!(vec![1, 2], [eq(1), eq(2)])
49 /// #     .unwrap();
50 /// ```
51 ///
52 /// Note: This behavior is only possible in [`verify_that!`] macros. In any
53 /// other cases, it is still necessary to use the
54 /// [`elements_are!`][crate::matchers::elements_are] macro.
55 ///
56 /// ```compile_fail
57 /// # use googletest::prelude::*;
58 /// verify_that!(vec![vec![1,2], vec![3]], [[eq(1), eq(2)], [eq(3)]])
59 /// # .unwrap();
60 /// ```
61 ///
62 /// Use this instead:
63 /// ```
64 /// # use googletest::prelude::*;
65 /// verify_that!(vec![vec![1,2], vec![3]], [elements_are![eq(1), eq(2)], elements_are![eq(3)]])
66 /// # .unwrap();
67 /// ```
68 ///
69 /// This matcher does not support matching directly against an [`Iterator`]. To
70 /// match against an iterator, use [`Iterator::collect`] to build a [`Vec`].
71 ///
72 /// Do not use this with unordered containers, since that will lead to flaky
73 /// tests. Use
74 /// [`unordered_elements_are!`][crate::matchers::unordered_elements_are]
75 /// instead.
76 ///
77 /// [`IntoIterator`]: std::iter::IntoIterator
78 /// [`Iterator`]: std::iter::Iterator
79 /// [`Iterator::collect`]: std::iter::Iterator::collect
80 /// [`Vec`]: std::vec::Vec
81 #[macro_export]
82 #[doc(hidden)]
83 macro_rules! __elements_are {
84     ($($matcher:expr),* $(,)?) => {{
85         use $crate::matchers::__internal_unstable_do_not_depend_on_these::ElementsAre;
86         ElementsAre::new(vec![$(Box::new($matcher)),*])
87     }}
88 }
89 
90 /// Module for use only by the procedural macros in this module.
91 ///
92 /// **For internal use only. API stablility is not guaranteed!**
93 #[doc(hidden)]
94 pub mod internal {
95     use crate::description::Description;
96     use crate::matcher::{Matcher, MatcherResult};
97     use crate::matcher_support::zipped_iterator::zip;
98     use std::{fmt::Debug, marker::PhantomData};
99 
100     /// This struct is meant to be used only by the macro `elements_are!`.
101     ///
102     /// **For internal use only. API stablility is not guaranteed!**
103     #[doc(hidden)]
104     pub struct ElementsAre<'a, ContainerT: ?Sized, T: Debug> {
105         elements: Vec<Box<dyn Matcher<ActualT = T> + 'a>>,
106         phantom: PhantomData<ContainerT>,
107     }
108 
109     impl<'a, ContainerT: ?Sized, T: Debug> ElementsAre<'a, ContainerT, T> {
110         /// Factory only intended for use in the macro `elements_are!`.
111         ///
112         /// **For internal use only. API stablility is not guaranteed!**
113         #[doc(hidden)]
new(elements: Vec<Box<dyn Matcher<ActualT = T> + 'a>>) -> Self114         pub fn new(elements: Vec<Box<dyn Matcher<ActualT = T> + 'a>>) -> Self {
115             Self { elements, phantom: Default::default() }
116         }
117     }
118 
119     impl<'a, T: Debug, ContainerT: Debug + ?Sized> Matcher for ElementsAre<'a, ContainerT, T>
120     where
121         for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
122     {
123         type ActualT = ContainerT;
124 
matches(&self, actual: &ContainerT) -> MatcherResult125         fn matches(&self, actual: &ContainerT) -> MatcherResult {
126             let mut zipped_iterator = zip(actual.into_iter(), self.elements.iter());
127             for (a, e) in zipped_iterator.by_ref() {
128                 if e.matches(a).is_no_match() {
129                     return MatcherResult::NoMatch;
130                 }
131             }
132             if !zipped_iterator.has_size_mismatch() {
133                 MatcherResult::Match
134             } else {
135                 MatcherResult::NoMatch
136             }
137         }
138 
explain_match(&self, actual: &ContainerT) -> Description139         fn explain_match(&self, actual: &ContainerT) -> Description {
140             let actual_iterator = actual.into_iter();
141             let mut zipped_iterator = zip(actual_iterator, self.elements.iter());
142             let mut mismatches = Vec::new();
143             for (idx, (a, e)) in zipped_iterator.by_ref().enumerate() {
144                 if e.matches(a).is_no_match() {
145                     mismatches.push(format!("element #{idx} is {a:?}, {}", e.explain_match(a)));
146                 }
147             }
148             if mismatches.is_empty() {
149                 if !zipped_iterator.has_size_mismatch() {
150                     "whose elements all match".into()
151                 } else {
152                     format!("whose size is {}", zipped_iterator.left_size()).into()
153                 }
154             } else if mismatches.len() == 1 {
155                 let mismatches = mismatches.into_iter().collect::<Description>();
156                 format!("where {mismatches}").into()
157             } else {
158                 let mismatches = mismatches.into_iter().collect::<Description>();
159                 format!("where:\n{}", mismatches.bullet_list().indent()).into()
160             }
161         }
162 
describe(&self, matcher_result: MatcherResult) -> Description163         fn describe(&self, matcher_result: MatcherResult) -> Description {
164             format!(
165                 "{} elements:\n{}",
166                 if matcher_result.into() { "has" } else { "doesn't have" },
167                 &self
168                     .elements
169                     .iter()
170                     .map(|matcher| matcher.describe(MatcherResult::Match))
171                     .collect::<Description>()
172                     .enumerate()
173                     .indent()
174             )
175             .into()
176         }
177     }
178 }
179