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 /// **Important**: The method should be pure function with a deterministic
43 /// output and no side effects. In particular, in the event of an assertion
44 /// failure, it will be invoked a second time, with the assertion failure output
45 /// reflecting the *second* invocation.
46 ///
47 /// If the method returns a *reference*, then it must be preceded by a `*`:
48 ///
49 /// ```
50 /// # use googletest::prelude::*;
51 /// # #[derive(Debug)]
52 /// # pub struct MyStruct {
53 /// #     a_field: u32,
54 /// # }
55 /// impl MyStruct {
56 ///     pub fn get_a_field(&self) -> &u32 { &self.a_field }
57 /// }
58 ///
59 /// # let value = vec![MyStruct { a_field: 100 }];
60 /// verify_that!(value, contains(property!(*MyStruct.get_a_field(), eq(100))))
61 /// #    .unwrap();
62 /// ```
63 ///
64 /// The method may also take additional arguments:
65 ///
66 /// ```
67 /// # use googletest::prelude::*;
68 /// # #[derive(Debug)]
69 /// # pub struct MyStruct {
70 /// #     a_field: u32,
71 /// # }
72 /// impl MyStruct {
73 ///     pub fn add_to_a_field(&self, a: u32) -> u32 { self.a_field + a }
74 /// }
75 ///
76 /// # let value = vec![MyStruct { a_field: 100 }];
77 /// verify_that!(value, contains(property!(MyStruct.add_to_a_field(50), eq(150))))
78 /// #    .unwrap();
79 /// ```
80 ///
81 /// Unfortunately, this matcher does *not* work with methods returning string
82 /// slices:
83 ///
84 /// ```compile_fail
85 /// # use googletest::prelude::*;
86 /// #[derive(Debug)]
87 /// pub struct MyStruct {
88 ///     a_string: String,
89 /// }
90 /// impl MyStruct {
91 ///     pub fn get_a_string(&self) -> &str { &self.a_string }
92 /// }
93 ///
94 /// let value = MyStruct { a_string: "A string".into() };
95 /// verify_that!(value, property!(*MyStruct.get_a_string(), eq("A string"))) // Does not compile
96 /// #    .unwrap();
97 /// ```
98 ///
99 /// This macro is analogous to [`field`][crate::matchers::field], except that it
100 /// extracts the datum to be matched from the given object by invoking a method
101 /// rather than accessing a field.
102 ///
103 /// The list of arguments may optionally have a trailing comma.
104 #[macro_export]
105 #[doc(hidden)]
106 macro_rules! __property {
107     ($($t:tt)*) => { $crate::property_internal!($($t)*) }
108 }
109 
110 // Internal-only macro created so that the macro definition does not appear in
111 // generated documentation.
112 #[doc(hidden)]
113 #[macro_export]
114 macro_rules! property_internal {
115     ($($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
116          use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher;
117         property_matcher(
118             |o: &$($t)::+| o.$method($($argument),*),
119             &stringify!($method($($argument),*)),
120             $m)
121     }};
122 
123     (* $($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
124         use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher;
125         property_ref_matcher(
126             |o: &$($t)::+| o.$method($($argument),*),
127             &stringify!($method($($argument),*)),
128             $m)
129     }};
130 }
131 
132 /// Items for use only by the declarative macros in this module.
133 ///
134 /// **For internal use only. API stablility is not guaranteed!**
135 #[doc(hidden)]
136 pub mod internal {
137     use crate::{
138         description::Description,
139         matcher::{Matcher, MatcherResult},
140     };
141     use std::{fmt::Debug, marker::PhantomData};
142 
143     /// **For internal use only. API stablility is not guaranteed!**
144     #[doc(hidden)]
property_matcher<OuterT: Debug, InnerT: Debug, MatcherT: Matcher<ActualT = InnerT>>( extractor: impl Fn(&OuterT) -> InnerT, property_desc: &'static str, inner: MatcherT, ) -> impl Matcher<ActualT = OuterT>145     pub fn property_matcher<OuterT: Debug, InnerT: Debug, MatcherT: Matcher<ActualT = InnerT>>(
146         extractor: impl Fn(&OuterT) -> InnerT,
147         property_desc: &'static str,
148         inner: MatcherT,
149     ) -> impl Matcher<ActualT = OuterT> {
150         PropertyMatcher { extractor, property_desc, inner, phantom: Default::default() }
151     }
152 
153     struct PropertyMatcher<OuterT, ExtractorT, MatcherT> {
154         extractor: ExtractorT,
155         property_desc: &'static str,
156         inner: MatcherT,
157         phantom: PhantomData<OuterT>,
158     }
159 
160     impl<InnerT, OuterT, ExtractorT, MatcherT> Matcher for PropertyMatcher<OuterT, ExtractorT, MatcherT>
161     where
162         InnerT: Debug,
163         OuterT: Debug,
164         ExtractorT: Fn(&OuterT) -> InnerT,
165         MatcherT: Matcher<ActualT = InnerT>,
166     {
167         type ActualT = OuterT;
168 
matches(&self, actual: &OuterT) -> MatcherResult169         fn matches(&self, actual: &OuterT) -> MatcherResult {
170             self.inner.matches(&(self.extractor)(actual))
171         }
172 
describe(&self, matcher_result: MatcherResult) -> Description173         fn describe(&self, matcher_result: MatcherResult) -> Description {
174             format!(
175                 "has property `{}`, which {}",
176                 self.property_desc,
177                 self.inner.describe(matcher_result)
178             )
179             .into()
180         }
181 
explain_match(&self, actual: &OuterT) -> Description182         fn explain_match(&self, actual: &OuterT) -> Description {
183             let actual_inner = (self.extractor)(actual);
184             format!(
185                 "whose property `{}` is `{:#?}`, {}",
186                 self.property_desc,
187                 actual_inner,
188                 self.inner.explain_match(&actual_inner)
189             )
190             .into()
191         }
192     }
193 
194     /// **For internal use only. API stablility is not guaranteed!**
195     #[doc(hidden)]
property_ref_matcher<OuterT, InnerT, MatcherT>( extractor: fn(&OuterT) -> &InnerT, property_desc: &'static str, inner: MatcherT, ) -> impl Matcher<ActualT = OuterT> where OuterT: Debug, InnerT: Debug + ?Sized, MatcherT: Matcher<ActualT = InnerT>,196     pub fn property_ref_matcher<OuterT, InnerT, MatcherT>(
197         extractor: fn(&OuterT) -> &InnerT,
198         property_desc: &'static str,
199         inner: MatcherT,
200     ) -> impl Matcher<ActualT = OuterT>
201     where
202         OuterT: Debug,
203         InnerT: Debug + ?Sized,
204         MatcherT: Matcher<ActualT = InnerT>,
205     {
206         PropertyRefMatcher { extractor, property_desc, inner }
207     }
208 
209     struct PropertyRefMatcher<InnerT: ?Sized, OuterT, MatcherT> {
210         extractor: fn(&OuterT) -> &InnerT,
211         property_desc: &'static str,
212         inner: MatcherT,
213     }
214 
215     impl<InnerT: Debug + ?Sized, OuterT: Debug, MatcherT: Matcher<ActualT = InnerT>> Matcher
216         for PropertyRefMatcher<InnerT, OuterT, MatcherT>
217     {
218         type ActualT = OuterT;
219 
matches(&self, actual: &OuterT) -> MatcherResult220         fn matches(&self, actual: &OuterT) -> MatcherResult {
221             self.inner.matches((self.extractor)(actual))
222         }
223 
describe(&self, matcher_result: MatcherResult) -> Description224         fn describe(&self, matcher_result: MatcherResult) -> Description {
225             format!(
226                 "has property `{}`, which {}",
227                 self.property_desc,
228                 self.inner.describe(matcher_result)
229             )
230             .into()
231         }
232 
explain_match(&self, actual: &OuterT) -> Description233         fn explain_match(&self, actual: &OuterT) -> Description {
234             let actual_inner = (self.extractor)(actual);
235             format!(
236                 "whose property `{}` is `{:#?}`, {}",
237                 self.property_desc,
238                 actual_inner,
239                 self.inner.explain_match(actual_inner)
240             )
241             .into()
242         }
243     }
244 }
245