googletest/matchers/property_matcher.rs
1// Copyright 2023 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 matcher module.
17#![doc(hidden)]
18
19/// Matches an object which, upon calling the given method on it with the given
20/// arguments, produces a value matched by the given inner matcher.
21///
22/// This is particularly useful as a nested matcher when the desired
23/// property cannot be accessed through a field and must instead be
24/// extracted through a method call. For example:
25///
26/// ```
27/// # use googletest::prelude::*;
28/// #[derive(Debug)]
29/// pub struct MyStruct {
30/// a_field: u32,
31/// }
32///
33/// impl MyStruct {
34/// pub fn get_a_field(&self) -> u32 { self.a_field }
35/// }
36///
37/// let value = vec![MyStruct { a_field: 100 }];
38/// verify_that!(value, contains(property!(&MyStruct.get_a_field(), eq(100))))
39/// # .unwrap();
40/// ```
41///
42///
43/// If the inner matcher is `eq(...)`, it can be omitted:
44///
45/// ```
46/// # use googletest::prelude::*;
47/// #[derive(Debug)]
48/// pub struct MyStruct {
49/// a_field: u32,
50/// }
51///
52/// impl MyStruct {
53/// pub fn get_a_field(&self) -> u32 { self.a_field }
54/// }
55///
56/// let value = vec![MyStruct { a_field: 100 }];
57/// verify_that!(value, contains(property!(&MyStruct.get_a_field(), 100)))
58/// # .unwrap();
59/// ```
60///
61/// **Important**: The method should be pure function with a deterministic
62/// output and no side effects. In particular, in the event of an assertion
63/// failure, it will be invoked a second time, with the assertion failure output
64/// reflecting the *second* invocation.
65///
66/// The method may also take additional litteral arguments:
67///
68/// ```
69/// # use googletest::prelude::*;
70/// # #[derive(Debug)]
71/// # pub struct MyStruct {
72/// # a_field: u32,
73/// # }
74/// impl MyStruct {
75/// pub fn add_to_a_field(&self, a: u32) -> u32 { self.a_field + a }
76/// }
77///
78/// # let value = vec![MyStruct { a_field: 100 }];
79/// verify_that!(value, contains(property!(&MyStruct.add_to_a_field(50), eq(150))))
80/// # .unwrap();
81/// ```
82///
83/// The arguments must be litteral as `property!` is not able to capture them.
84///
85/// # Specification of the property pattern
86///
87/// The specification of the field follow the syntax: `(ref)? (&)?
88/// $TYPE.$PROPERTY\($ARGUMENT\)`.
89///
90/// The `&` allows to specify whether this matcher matches against an actual of
91/// type `$TYPE` (`$TYPE` must implement `Copy`) or a `&$TYPE`.
92///
93/// For instance:
94///
95/// ```
96/// # use googletest::prelude::*;
97/// #[derive(Debug)]
98/// pub struct AStruct;
99///
100/// impl AStruct {
101/// fn a_property(&self) -> i32 {32}
102/// }
103/// # fn should_pass() -> Result<()> {
104/// verify_that!(AStruct, property!(&AStruct.a_property(), eq(32)))?;
105/// # Ok(())
106/// # }
107/// # should_pass().unwrap();
108/// ```
109///
110/// ```
111/// # use googletest::prelude::*;
112/// #[derive(Debug, Clone, Copy)]
113/// pub struct AStruct;
114///
115/// impl AStruct {
116/// fn a_property(self) -> i32 {32}
117/// }
118/// # fn should_pass() -> Result<()> {
119/// verify_that!(AStruct, property!(AStruct.a_property(), eq(32)))?;
120/// # Ok(())
121/// # }
122/// # should_pass().unwrap();
123/// ```
124///
125/// The `ref` allows to bind the property returned value by reference, which is
126/// required if the field type does not implement `Copy`.
127///
128/// For instance:
129///
130/// ```
131/// # use googletest::prelude::*;
132/// #[derive(Debug)]
133/// pub struct AStruct;
134///
135/// impl AStruct {
136/// fn a_property(&self) -> i32 {32}
137/// }
138/// # fn should_pass() -> Result<()> {
139/// verify_that!(AStruct, property!(&AStruct.a_property(), eq(32)))?;
140/// # Ok(())
141/// # }
142/// # should_pass().unwrap();
143/// ```
144///
145/// If `property!` is qualified by both `&` and `ref`, they can both be omitted.
146///
147/// ```
148/// # use googletest::prelude::*;
149/// #[derive(Debug)]
150/// pub struct AStruct;
151///
152/// impl AStruct {
153/// fn a_property(&self) -> String {"32".into()}
154/// }
155/// # fn should_pass() -> Result<()> {
156/// verify_that!(AStruct, property!(&AStruct.a_property(), ref eq("32")))?;
157/// verify_that!(AStruct, property!(AStruct.a_property(), eq("32")))?;
158/// # Ok(())
159/// # }
160/// # should_pass().unwrap();
161/// ```
162///
163/// This macro is analogous to [`field`][crate::matchers::field], except that it
164/// extracts the datum to be matched from the given object by invoking a method
165/// rather than accessing a field.
166///
167/// The list of arguments may optionally have a trailing comma.
168#[macro_export]
169#[doc(hidden)]
170macro_rules! __property {
171 ($($t:tt)*) => { $crate::property_internal!($($t)*) }
172}
173
174// Internal-only macro created so that the macro definition does not appear in
175// generated documentation.
176#[doc(hidden)]
177#[macro_export]
178macro_rules! property_internal {
179 // `ref` variant.
180 (
181 // The 3 cases of `$(& $($_amp:literal)?)?` -> `$(& $($_amp)*)*`
182 // 1. outer group captures nothing => expansion produces nothing
183 // 2. outer group captures just `&` => expansion produces `&`
184 // 3. outer group captures `& <literal>` => disallowed by `@assert_empty` subrule invocation
185 //
186 // `$_amp:literal` works only because the following `$t:ident` or `::` can't be captured by
187 // it.
188 $(& $($_amp:literal)?)? // Optional `&`'s presence is implicitly captured by `_amp`.
189 $(:: $($_cs:literal)?)? // Optional `::`'s presence is implicitly captured by `_cs`.
190 $($t:ident)::+ $(::<$($t_ty_args:ty),* $(,)?>)?
191 .$method:tt $(::<$($m_ty_args:ty),* $(,)?>)? ($($argument:expr),* $(,)?),
192 ref $m:expr
193 ) => {{
194 $crate::property_internal!(@assert_empty $($($_amp)*)* $($($_cs)*)*);
195 $crate::property_internal!(
196 @self_arg
197 struct_type: [$(& $($_amp)*)* $(:: $($_cs)*)* $($t)::+ $( <$($t_ty_args),*>)*]
198 method_prefix: [ $(:: $($_cs)*)* $($t)::+ $(::<$($t_ty_args),*>)*]
199 [$method $(::<$($m_ty_args),*>)*] [$($argument),*] [$m])
200 }};
201
202 // Non-`ref` variant.
203 (
204 // See comment on previous variant above.
205 $(& $($_amp:literal)?)? // Optional `&`'s presence is implicitly captured by `_amp`.
206 $(:: $($_cs:literal)?)? // Optional `::`'s presence is implicitly captured by `_cs`.
207 $($t:ident)::+ $(::<$($t_ty_args:ty),* $(,)?>)?
208 .$method:tt $(::<$($m_ty_args:ty),* $(,)?>)? ($($argument:expr),* $(,)?),
209 $m:expr) => {{
210 $crate::property_internal!(@assert_empty $($($_amp)*)* $($($_cs)*)*);
211 $crate::property_internal!(
212 @self_dot
213 struct_type: [$(& $($_amp)*)* $(:: $($_cs)*)* $($t)::+ $(<$($t_ty_args),*>)*]
214 [$method $(::<$($m_ty_args),*>)*] [$($argument),*] [$m])
215 }};
216
217 (@assert_empty) => {};
218 (@assert_empty $($l:literal)+) => {
219 compile_error!("property! argument must start with an optional `&` followed by a path")
220 };
221
222 (@self_arg struct_type: [$struct_ty:ty]
223 method_prefix: [$($method_prefix:tt)+]
224 [$($method:tt)*] [$($argument:expr),*] [$m:expr]) => {{
225 $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher(
226 |o: $struct_ty| $($method_prefix)*::$($method)* (o, $($argument),*),
227 &stringify!($($method)* ($($argument),*)),
228 $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
229 }};
230
231 (@self_dot struct_type: [$struct_ty:ty]
232 [$($method:tt)*] [$($argument:expr),*] [$m:expr]) => {{
233 $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher(
234 |o: &$struct_ty| o.$($method)* ($($argument),*),
235 &stringify!($($method)* ($($argument),*)),
236 $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
237 }};
238}
239
240/// Items for use only by the declarative macros in this module.
241///
242/// **For internal use only. API stablility is not guaranteed!**
243#[doc(hidden)]
244pub mod internal {
245 use crate::{
246 description::Description,
247 matcher::{Matcher, MatcherBase, MatcherResult},
248 };
249 use std::fmt::Debug;
250
251 /// **For internal use only. API stablility is not guaranteed!**
252 #[doc(hidden)]
253 pub fn property_matcher<OuterT: Debug, InnerT: Debug, MatcherT>(
254 extractor: fn(&OuterT) -> InnerT,
255 property_desc: &'static str,
256 inner: MatcherT,
257 ) -> PropertyMatcher<OuterT, InnerT, MatcherT> {
258 PropertyMatcher { extractor, property_desc, inner }
259 }
260
261 #[derive(MatcherBase)]
262 pub struct PropertyMatcher<OuterT, InnerT, MatcherT> {
263 extractor: fn(&OuterT) -> InnerT,
264 property_desc: &'static str,
265 inner: MatcherT,
266 }
267
268 impl<InnerT, OuterT, MatcherT> Matcher<OuterT> for PropertyMatcher<OuterT, InnerT, MatcherT>
269 where
270 InnerT: Debug + Copy,
271 OuterT: Debug + Copy,
272 MatcherT: Matcher<InnerT>,
273 {
274 fn matches(&self, actual: OuterT) -> MatcherResult {
275 self.inner.matches((self.extractor)(&actual))
276 }
277
278 fn describe(&self, matcher_result: MatcherResult) -> Description {
279 format!(
280 "has property `{}`, which {}",
281 self.property_desc,
282 self.inner.describe(matcher_result)
283 )
284 .into()
285 }
286
287 fn explain_match(&self, actual: OuterT) -> Description {
288 let actual_inner = (self.extractor)(&actual);
289 format!(
290 "whose property `{}` is `{:#?}`, {}",
291 self.property_desc,
292 actual_inner,
293 self.inner.explain_match(actual_inner)
294 )
295 .into()
296 }
297 }
298
299 impl<'a, InnerT, OuterT, MatcherT> Matcher<&'a OuterT> for PropertyMatcher<OuterT, InnerT, MatcherT>
300 where
301 InnerT: Debug,
302 OuterT: Debug,
303 MatcherT: for<'b> Matcher<&'b InnerT>,
304 {
305 fn matches(&self, actual: &'a OuterT) -> MatcherResult {
306 self.inner.matches(&(self.extractor)(actual))
307 }
308
309 fn describe(&self, matcher_result: MatcherResult) -> Description {
310 format!(
311 "has property `{}`, which {}",
312 self.property_desc,
313 self.inner.describe(matcher_result)
314 )
315 .into()
316 }
317
318 fn explain_match(&self, actual: &'a OuterT) -> Description {
319 let actual_inner = (self.extractor)(actual);
320 format!(
321 "whose property `{}` is `{:#?}`, {}",
322 self.property_desc,
323 actual_inner,
324 self.inner.explain_match(&actual_inner)
325 )
326 .into()
327 }
328 }
329
330 /// **For internal use only. API stablility is not guaranteed!**
331 #[doc(hidden)]
332 pub fn property_ref_matcher<OuterT, InnerT, ExtractorT, MatcherT>(
333 extractor: ExtractorT,
334 property_desc: &'static str,
335 inner: MatcherT,
336 ) -> PropertyRefMatcher<ExtractorT, MatcherT>
337 where
338 OuterT: Debug,
339 InnerT: Debug,
340 MatcherT: for<'a> Matcher<&'a InnerT>,
341 ExtractorT: Fn(OuterT) -> InnerT,
342 {
343 PropertyRefMatcher { extractor, property_desc, inner }
344 }
345
346 #[derive(MatcherBase)]
347 pub struct PropertyRefMatcher<ExtractorT, MatcherT> {
348 extractor: ExtractorT,
349 property_desc: &'static str,
350 inner: MatcherT,
351 }
352
353 impl<
354 InnerT: Debug,
355 OuterT: Debug + Copy,
356 MatcherT: for<'a> Matcher<&'a InnerT>,
357 ExtractorT: Fn(OuterT) -> InnerT,
358 > Matcher<OuterT> for PropertyRefMatcher<ExtractorT, MatcherT>
359 {
360 fn matches(&self, actual: OuterT) -> MatcherResult {
361 self.inner.matches(&(self.extractor)(actual))
362 }
363
364 fn describe(&self, matcher_result: MatcherResult) -> Description {
365 format!(
366 "has property `{}`, which {}",
367 self.property_desc,
368 self.inner.describe(matcher_result)
369 )
370 .into()
371 }
372
373 fn explain_match(&self, actual: OuterT) -> Description {
374 let actual_inner = (self.extractor)(actual);
375 format!(
376 "whose property `{}` is `{:#?}`, {}",
377 self.property_desc,
378 actual_inner,
379 self.inner.explain_match(&actual_inner)
380 )
381 .into()
382 }
383 }
384}