Skip to main content

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}