Skip to content

Commit db5c0e9

Browse files
Merge pull request google#378 from google:binding_mode
PiperOrigin-RevId: 626324639
2 parents e970af1 + 9854cbd commit db5c0e9

File tree

7 files changed

+184
-92
lines changed

7 files changed

+184
-92
lines changed

googletest/src/matchers/field_matcher.rs

Lines changed: 27 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,15 @@
152152
/// # should_pass().unwrap();
153153
/// ```
154154
///
155+
/// If `field!` is qualified by both `&` and `ref`, they can both be omitted.
156+
///
155157
/// ```
156158
/// # use googletest::prelude::*;
157159
/// #[derive(Debug)]
158160
/// pub struct AStruct{a_field: String};
159161
/// # fn should_pass() -> Result<()> {
160162
/// verify_that!(AStruct{a_field: "32".into()}, field!(&AStruct.a_field, ref eq("32")))?;
163+
/// verify_that!(AStruct{a_field: "32".into()}, field!(AStruct.a_field, eq("32")))?;
161164
/// # Ok(())
162165
/// # }
163166
/// # should_pass().unwrap();
@@ -173,12 +176,14 @@ macro_rules! __field {
173176

174177
// Internal-only macro created so that the macro definition does not appear in
175178
// generated documentation.
179+
// We cannot use `path` or `ty` to capture the type as we are terminating the
180+
// type with a . (dot).
176181
#[doc(hidden)]
177182
#[macro_export]
178183
macro_rules! field_internal {
179184
(&$($t:ident)::+.$field:tt, ref $m:expr) => {{
180-
use $crate::matchers::__internal_unstable_do_not_depend_on_these::field_ref_matcher;
181-
field_ref_matcher(
185+
use $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher;
186+
field_matcher(
182187
|o: &_| {
183188
match o {
184189
&$($t)::* {$field: ref value, .. } => Some(value),
@@ -195,7 +200,7 @@ macro_rules! field_internal {
195200
(&$($t:ident)::+.$field:tt, $m:expr) => {{
196201
use $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher;
197202
field_matcher(
198-
|o: &_| {
203+
|o: &&_| {
199204
match o {
200205
&$($t)::* {$field: value, .. } => Some(value),
201206
// The pattern below is unreachable if the type is a struct (as opposed to an
@@ -211,7 +216,7 @@ macro_rules! field_internal {
211216
($($t:ident)::+.$field:tt, $m:expr) => {{
212217
use $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher;
213218
field_matcher(
214-
|o| {
219+
|o: &_| {
215220
match o {
216221
$($t)::* {$field: value, .. } => Some(value),
217222
// The pattern below is unreachable if the type is a struct (as opposed to an
@@ -242,36 +247,33 @@ pub mod internal {
242247
///
243248
/// **For internal use only. API stablility is not guaranteed!**
244249
#[doc(hidden)]
245-
pub fn field_matcher<ExtractorT, InnerMatcher>(
246-
field_accessor: ExtractorT,
250+
pub fn field_matcher<OuterT, InnerT, InnerMatcher>(
251+
field_accessor: fn(&OuterT) -> Option<&InnerT>,
247252
field_path: &'static str,
248253
inner: InnerMatcher,
249-
) -> FieldMatcher<ExtractorT, InnerMatcher> {
254+
) -> FieldMatcher<OuterT, InnerT, InnerMatcher> {
250255
FieldMatcher { field_accessor, field_path, inner }
251256
}
252257

253258
#[derive(MatcherBase)]
254-
pub struct FieldMatcher<ExtractorT, InnerMatcher> {
255-
field_accessor: ExtractorT,
259+
pub struct FieldMatcher<OuterT, InnerT, InnerMatcher> {
260+
field_accessor: fn(&OuterT) -> Option<&InnerT>,
256261
field_path: &'static str,
257262
inner: InnerMatcher,
258263
}
259-
impl<
260-
OuterT: Debug + Copy,
261-
InnerT: Debug + Copy,
262-
ExtractorT: Fn(OuterT) -> Option<InnerT>,
263-
InnerMatcher: Matcher<InnerT>,
264-
> Matcher<OuterT> for FieldMatcher<ExtractorT, InnerMatcher>
264+
265+
impl<'a, OuterT: Debug + 'a, InnerT: Debug + 'a, InnerMatcher: Matcher<&'a InnerT>>
266+
Matcher<&'a OuterT> for FieldMatcher<OuterT, InnerT, InnerMatcher>
265267
{
266-
fn matches(&self, actual: OuterT) -> MatcherResult {
268+
fn matches(&self, actual: &'a OuterT) -> MatcherResult {
267269
if let Some(value) = (self.field_accessor)(actual) {
268270
self.inner.matches(value)
269271
} else {
270272
MatcherResult::NoMatch
271273
}
272274
}
273275

274-
fn explain_match(&self, actual: OuterT) -> Description {
276+
fn explain_match(&self, actual: &'a OuterT) -> Description {
275277
if let Some(actual) = (self.field_accessor)(actual) {
276278
format!(
277279
"which has field `{}`, {}",
@@ -297,49 +299,23 @@ pub mod internal {
297299
}
298300
}
299301

300-
#[doc(hidden)]
301-
pub fn field_ref_matcher<
302-
InnerT,
303-
OuterT,
304-
ExtractorT: for<'a> Fn(&'a OuterT) -> Option<&'a InnerT>,
305-
InnerMatcher,
306-
>(
307-
field_accessor: ExtractorT,
308-
field_path: &'static str,
309-
inner: InnerMatcher,
310-
) -> FieldMatcher<ExtractorT, InnerMatcher> {
311-
FieldMatcher { field_accessor, field_path, inner }
312-
}
313-
314-
#[derive(MatcherBase)]
315-
pub struct FieldRefMatcher<ExtractorT, InnerMatcher> {
316-
field_accessor: ExtractorT,
317-
field_path: &'static str,
318-
inner: InnerMatcher,
319-
}
320-
321-
impl<
322-
'a,
323-
OuterT: Debug + 'a,
324-
InnerT: Debug + 'a,
325-
ExtractorT: Fn(&'a OuterT) -> Option<&'a InnerT>,
326-
InnerMatcher: Matcher<&'a InnerT>,
327-
> Matcher<&'a OuterT> for FieldRefMatcher<ExtractorT, InnerMatcher>
302+
impl<OuterT: Debug + Copy, InnerT: Debug + Copy, InnerMatcher: Matcher<InnerT>> Matcher<OuterT>
303+
for FieldMatcher<OuterT, InnerT, InnerMatcher>
328304
{
329-
fn matches(&self, actual: &'a OuterT) -> MatcherResult {
330-
if let Some(value) = (self.field_accessor)(actual) {
331-
self.inner.matches(value)
305+
fn matches(&self, actual: OuterT) -> MatcherResult {
306+
if let Some(value) = (self.field_accessor)(&actual) {
307+
self.inner.matches(*value)
332308
} else {
333309
MatcherResult::NoMatch
334310
}
335311
}
336312

337-
fn explain_match(&self, actual: &'a OuterT) -> Description {
338-
if let Some(actual) = (self.field_accessor)(actual) {
313+
fn explain_match(&self, actual: OuterT) -> Description {
314+
if let Some(actual) = (self.field_accessor)(&actual) {
339315
format!(
340316
"which has field `{}`, {}",
341317
self.field_path,
342-
self.inner.explain_match(actual)
318+
self.inner.explain_match(*actual)
343319
)
344320
.into()
345321
} else {

googletest/src/matchers/matches_pattern.rs

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@
3737
/// a_field: "Something to believe in".into(),
3838
/// another_field: "Something else".into()
3939
/// };
40-
/// verify_that!(my_struct, matches_pattern!(&MyStruct {
41-
/// a_field: ref starts_with("Something"),
42-
/// another_field: ref ends_with("else"),
40+
/// verify_that!(my_struct, matches_pattern!(MyStruct {
41+
/// a_field: starts_with("Something"),
42+
/// another_field: ends_with("else"),
4343
/// }))
4444
/// # .unwrap();
4545
/// ```
@@ -59,8 +59,8 @@
5959
/// # a_field: "Something to believe in".into(),
6060
/// # another_field: "Something else".into()
6161
/// # };
62-
/// verify_that!(my_struct, matches_pattern!(&MyStruct {
63-
/// a_field: ref starts_with("Something"),
62+
/// verify_that!(my_struct, matches_pattern!(MyStruct {
63+
/// a_field: starts_with("Something"),
6464
/// // another_field is missing, so it may be anything.
6565
/// }))
6666
/// # .unwrap();
@@ -83,9 +83,9 @@
8383
/// let my_struct = MyStruct {
8484
/// a_nested_struct: MyInnerStruct { a_field: "Something to believe in".into() },
8585
/// };
86-
/// verify_that!(my_struct, matches_pattern!(&MyStruct {
87-
/// a_nested_struct: ref matches_pattern!(&MyInnerStruct {
88-
/// a_field: ref starts_with("Something"),
86+
/// verify_that!(my_struct, matches_pattern!(MyStruct {
87+
/// a_nested_struct: matches_pattern!(MyInnerStruct {
88+
/// a_field: starts_with("Something"),
8989
/// }),
9090
/// }))
9191
/// # .unwrap();
@@ -109,9 +109,9 @@
109109
/// # let my_struct = MyStruct {
110110
/// # a_nested_struct: MyInnerStruct { a_field: "Something to believe in".into() },
111111
/// # };
112-
/// verify_that!(my_struct, matches_pattern!(&MyStruct {
113-
/// a_nested_struct: ref pat!(&MyInnerStruct {
114-
/// a_field: ref starts_with("Something"),
112+
/// verify_that!(my_struct, matches_pattern!(MyStruct {
113+
/// a_nested_struct: pat!(MyInnerStruct {
114+
/// a_field: starts_with("Something"),
115115
/// }),
116116
/// }))
117117
/// # .unwrap();
@@ -132,8 +132,8 @@
132132
/// }
133133
///
134134
/// let my_struct = MyStruct { a_field: "Something to believe in".into() };
135-
/// verify_that!(my_struct, matches_pattern!(&MyStruct {
136-
/// get_a_field(): ref starts_with("Something"),
135+
/// verify_that!(my_struct, matches_pattern!(MyStruct {
136+
/// get_a_field(): starts_with("Something"),
137137
/// }))
138138
/// # .unwrap();
139139
/// ```
@@ -143,7 +143,7 @@
143143
/// failure, it will be invoked a second time, with the assertion failure output
144144
/// reflecting the *second* invocation.
145145
///
146-
/// These may also include extra parameters you pass in:
146+
/// These may also include extra litteral parameters you pass in:
147147
///
148148
/// ```
149149
/// # use googletest::prelude::*;
@@ -163,7 +163,7 @@
163163
/// # .unwrap();
164164
/// ```
165165
///
166-
/// If the method returns a non-`Copy` type, precede it with a `ref` to bind the
166+
/// You can precede both field and property matchers with a `ref` to match the
167167
/// result by reference:
168168
///
169169
/// ```
@@ -184,6 +184,28 @@
184184
/// # .unwrap();
185185
/// ```
186186
///
187+
/// Note that if the `actual` is of type `&ActualT` and the pattern type is
188+
/// `ActualT`, this is automatically performed. This behavior is similar to the
189+
/// reference binding mode in pattern matching.
190+
///
191+
/// ```
192+
/// # use googletest::prelude::*;
193+
/// # #[derive(Debug)]
194+
/// # struct MyStruct {
195+
/// # a_field: String,
196+
/// # }
197+
/// #
198+
/// impl MyStruct {
199+
/// fn get_a_field_ref(&self) -> String { self.a_field.clone() }
200+
/// }
201+
///
202+
/// # let my_struct = MyStruct { a_field: "Something to believe in".into() };
203+
/// verify_that!(my_struct, matches_pattern!(MyStruct {
204+
/// get_a_field_ref(): starts_with("Something"),
205+
/// }))
206+
/// # .unwrap();
207+
/// ```
208+
///
187209
/// One can also match tuple structs with up to 10 fields. In this case, all
188210
/// fields must have matchers:
189211
///

googletest/src/matchers/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ pub mod __internal_unstable_do_not_depend_on_these {
108108
pub use super::conjunction_matcher::ConjunctionMatcher;
109109
pub use super::disjunction_matcher::DisjunctionMatcher;
110110
pub use super::elements_are_matcher::internal::ElementsAre;
111-
pub use super::field_matcher::internal::{field_matcher, field_ref_matcher};
111+
pub use super::field_matcher::internal::field_matcher;
112112
pub use super::is_matcher::is;
113113
pub use super::pointwise_matcher::internal::PointwiseMatcher;
114114
pub use super::property_matcher::internal::{property_matcher, property_ref_matcher};

0 commit comments

Comments
 (0)