// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // There are no visible documentation elements in this module; the declarative // macro is documented in the matcher module. #![doc(hidden)] /// Matches an object which, upon calling the given method on it with the given /// arguments, produces a value matched by the given inner matcher. /// /// This is particularly useful as a nested matcher when the desired /// property cannot be accessed through a field and must instead be /// extracted through a method call. For example: /// /// ``` /// # use googletest::prelude::*; /// #[derive(Debug)] /// pub struct MyStruct { /// a_field: u32, /// } /// /// impl MyStruct { /// pub fn get_a_field(&self) -> u32 { self.a_field } /// } /// /// let value = vec![MyStruct { a_field: 100 }]; /// verify_that!(value, contains(property!(MyStruct.get_a_field(), eq(100)))) /// # .unwrap(); /// ``` /// /// **Important**: The method should be pure function with a deterministic /// output and no side effects. In particular, in the event of an assertion /// failure, it will be invoked a second time, with the assertion failure output /// reflecting the *second* invocation. /// /// If the method returns a *reference*, then it must be preceded by a `*`: /// /// ``` /// # use googletest::prelude::*; /// # #[derive(Debug)] /// # pub struct MyStruct { /// # a_field: u32, /// # } /// impl MyStruct { /// pub fn get_a_field(&self) -> &u32 { &self.a_field } /// } /// /// # let value = vec![MyStruct { a_field: 100 }]; /// verify_that!(value, contains(property!(*MyStruct.get_a_field(), eq(100)))) /// # .unwrap(); /// ``` /// /// The method may also take additional arguments: /// /// ``` /// # use googletest::prelude::*; /// # #[derive(Debug)] /// # pub struct MyStruct { /// # a_field: u32, /// # } /// impl MyStruct { /// pub fn add_to_a_field(&self, a: u32) -> u32 { self.a_field + a } /// } /// /// # let value = vec![MyStruct { a_field: 100 }]; /// verify_that!(value, contains(property!(MyStruct.add_to_a_field(50), eq(150)))) /// # .unwrap(); /// ``` /// /// Unfortunately, this matcher does *not* work with methods returning string /// slices: /// /// ```compile_fail /// # use googletest::prelude::*; /// #[derive(Debug)] /// pub struct MyStruct { /// a_string: String, /// } /// impl MyStruct { /// pub fn get_a_string(&self) -> &str { &self.a_string } /// } /// /// let value = MyStruct { a_string: "A string".into() }; /// verify_that!(value, property!(*MyStruct.get_a_string(), eq("A string"))) // Does not compile /// # .unwrap(); /// ``` /// /// This macro is analogous to [`field`][crate::matchers::field], except that it /// extracts the datum to be matched from the given object by invoking a method /// rather than accessing a field. /// /// The list of arguments may optionally have a trailing comma. #[macro_export] #[doc(hidden)] macro_rules! __property { ($($t:tt)*) => { $crate::property_internal!($($t)*) } } // Internal-only macro created so that the macro definition does not appear in // generated documentation. #[doc(hidden)] #[macro_export] macro_rules! property_internal { ($($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{ use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher; property_matcher( |o: &$($t)::+| o.$method($($argument),*), &stringify!($method($($argument),*)), $m) }}; (* $($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{ use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher; property_ref_matcher( |o: &$($t)::+| o.$method($($argument),*), &stringify!($method($($argument),*)), $m) }}; } /// Items for use only by the declarative macros in this module. /// /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] pub mod internal { use crate::{ description::Description, matcher::{Matcher, MatcherResult}, }; use std::{fmt::Debug, marker::PhantomData}; /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] pub fn property_matcher>( extractor: impl Fn(&OuterT) -> InnerT, property_desc: &'static str, inner: MatcherT, ) -> impl Matcher { PropertyMatcher { extractor, property_desc, inner, phantom: Default::default() } } struct PropertyMatcher { extractor: ExtractorT, property_desc: &'static str, inner: MatcherT, phantom: PhantomData, } impl Matcher for PropertyMatcher where InnerT: Debug, OuterT: Debug, ExtractorT: Fn(&OuterT) -> InnerT, MatcherT: Matcher, { type ActualT = OuterT; fn matches(&self, actual: &OuterT) -> MatcherResult { self.inner.matches(&(self.extractor)(actual)) } fn describe(&self, matcher_result: MatcherResult) -> Description { format!( "has property `{}`, which {}", self.property_desc, self.inner.describe(matcher_result) ) .into() } fn explain_match(&self, actual: &OuterT) -> Description { let actual_inner = (self.extractor)(actual); format!( "whose property `{}` is `{:#?}`, {}", self.property_desc, actual_inner, self.inner.explain_match(&actual_inner) ) .into() } } /// **For internal use only. API stablility is not guaranteed!** #[doc(hidden)] pub fn property_ref_matcher( extractor: fn(&OuterT) -> &InnerT, property_desc: &'static str, inner: MatcherT, ) -> impl Matcher where OuterT: Debug, InnerT: Debug + ?Sized, MatcherT: Matcher, { PropertyRefMatcher { extractor, property_desc, inner } } struct PropertyRefMatcher { extractor: fn(&OuterT) -> &InnerT, property_desc: &'static str, inner: MatcherT, } impl> Matcher for PropertyRefMatcher { type ActualT = OuterT; fn matches(&self, actual: &OuterT) -> MatcherResult { self.inner.matches((self.extractor)(actual)) } fn describe(&self, matcher_result: MatcherResult) -> Description { format!( "has property `{}`, which {}", self.property_desc, self.inner.describe(matcher_result) ) .into() } fn explain_match(&self, actual: &OuterT) -> Description { let actual_inner = (self.extractor)(actual); format!( "whose property `{}` is `{:#?}`, {}", self.property_desc, actual_inner, self.inner.explain_match(actual_inner) ) .into() } } }