Skip to main content

test_that/matchers/
tuple_matcher.rs

1// Copyright 2022 Google LLC
2// Copyright 2026 Bradford Hovinen <bradford@hovinen.me>
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16// There are no visible documentation elements in this module; the declarative
17// macro is documented at the top level.
18#![doc(hidden)]
19
20/// Functions for use only by the declarative macros in this module.
21///
22/// **For internal use only. API stablility is not guaranteed!**
23#[doc(hidden)]
24pub mod __internal {
25    use crate::{
26        description::Description,
27        matcher::{Describable, Matcher, MatcherResult},
28    };
29    use core::fmt::Debug;
30
31    // This implementation is provided for completeness, but is completely trivial.
32    // The only actual value which can be supplied is (), which must match.
33    impl Matcher<()> for () {
34        fn matches(&self, _: &()) -> MatcherResult {
35            MatcherResult::Match
36        }
37    }
38
39    impl Describable for () {
40        fn describe(&self, matcher_result: MatcherResult) -> Description {
41            match matcher_result {
42                MatcherResult::Match => "is the empty tuple".into(),
43                MatcherResult::NoMatch => "is not the empty tuple".into(),
44            }
45        }
46    }
47
48    /// Generates a tuple matcher for tuples of a specific length.
49    ///
50    /// **For internal use only. API stablility is not guaranteed!**
51    #[doc(hidden)]
52    macro_rules! tuple_matcher_n {
53        ($([$field_number:tt, $matcher_type:ident, $field_type:ident]),*) => {
54            impl<$($field_type: Debug, $matcher_type: Matcher<$field_type>),*>
55                Matcher<($($field_type,)*)> for ($($matcher_type,)*)
56            {
57                fn matches(&self, actual: &($($field_type,)*)) -> MatcherResult {
58                    $(match self.$field_number.matches(&actual.$field_number) {
59                        MatcherResult::Match => {},
60                        MatcherResult::NoMatch => {
61                            return MatcherResult::NoMatch;
62                        }
63                    })*
64                    MatcherResult::Match
65                }
66
67                fn explain_match(&self, actual: &($($field_type,)*)) -> Description {
68                    let mut explanation = Description::new().text("which").nested(self.describe(self.matches(actual)));
69                    $(match self.$field_number.matches(&actual.$field_number) {
70                        MatcherResult::Match => {},
71                        MatcherResult::NoMatch => {
72                            explanation = explanation
73                                .text(format!(concat!("Element #", $field_number, " is {:?},"), actual.$field_number))
74                                .nested(self.$field_number.explain_match(&actual.$field_number));
75                        }
76                    })*
77                    explanation
78                }
79
80            }
81
82            impl<$($matcher_type: Describable),*> Describable for ($($matcher_type,)*) {
83                fn describe(&self, matcher_result: MatcherResult) -> Description {
84                    match matcher_result {
85                        MatcherResult::Match => {
86                            let mut description = Description::new().text("is a tuple whose values respectively match:");
87                            $(description = description.nested(self.$field_number.describe(matcher_result));)*
88                            description
89                        }
90                        MatcherResult::NoMatch => {
91                            let mut description = Description::new().text("is a tuple whose values do not respectively match:");
92                            $(description = description.nested(self.$field_number.describe(MatcherResult::Match));)*
93                            description
94                        }
95                    }
96                }
97            }
98        };
99    }
100
101    tuple_matcher_n!([0, I0, T0]);
102
103    tuple_matcher_n!([0, I0, T0], [1, I1, T1]);
104
105    tuple_matcher_n!([0, I0, T0], [1, I1, T1], [2, I2, T2]);
106
107    tuple_matcher_n!([0, I0, T0], [1, I1, T1], [2, I2, T2], [3, I3, T3]);
108
109    tuple_matcher_n!([0, I0, T0], [1, I1, T1], [2, I2, T2], [3, I3, T3], [4, I4, T4]);
110
111    tuple_matcher_n!([0, I0, T0], [1, I1, T1], [2, I2, T2], [3, I3, T3], [4, I4, T4], [5, I5, T5]);
112
113    tuple_matcher_n!(
114        [0, I0, T0],
115        [1, I1, T1],
116        [2, I2, T2],
117        [3, I3, T3],
118        [4, I4, T4],
119        [5, I5, T5],
120        [6, I6, T6]
121    );
122
123    tuple_matcher_n!(
124        [0, I0, T0],
125        [1, I1, T1],
126        [2, I2, T2],
127        [3, I3, T3],
128        [4, I4, T4],
129        [5, I5, T5],
130        [6, I6, T6],
131        [7, I7, T7]
132    );
133
134    tuple_matcher_n!(
135        [0, I0, T0],
136        [1, I1, T1],
137        [2, I2, T2],
138        [3, I3, T3],
139        [4, I4, T4],
140        [5, I5, T5],
141        [6, I6, T6],
142        [7, I7, T7],
143        [8, I8, T8]
144    );
145
146    tuple_matcher_n!(
147        [0, I0, T0],
148        [1, I1, T1],
149        [2, I2, T2],
150        [3, I3, T3],
151        [4, I4, T4],
152        [5, I5, T5],
153        [6, I6, T6],
154        [7, I7, T7],
155        [8, I8, T8],
156        [9, I9, T9]
157    );
158
159    tuple_matcher_n!(
160        [0, I0, T0],
161        [1, I1, T1],
162        [2, I2, T2],
163        [3, I3, T3],
164        [4, I4, T4],
165        [5, I5, T5],
166        [6, I6, T6],
167        [7, I7, T7],
168        [8, I8, T8],
169        [9, I9, T9],
170        [10, I10, T10]
171    );
172
173    tuple_matcher_n!(
174        [0, I0, T0],
175        [1, I1, T1],
176        [2, I2, T2],
177        [3, I3, T3],
178        [4, I4, T4],
179        [5, I5, T5],
180        [6, I6, T6],
181        [7, I7, T7],
182        [8, I8, T8],
183        [9, I9, T9],
184        [10, I10, T10],
185        [11, I11, T11]
186    );
187}