1 // Copyright 2022 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 matchers module. 17 #![doc(hidden)] 18 19 /// Matches a structure or enum with a given field which is matched by a given 20 /// matcher. 21 /// 22 /// This takes two arguments: 23 /// 24 /// * a specification of the field against which to match, and 25 /// * an inner [`Matcher`][crate::matcher::Matcher] to apply to that field. 26 /// 27 /// For example: 28 /// 29 /// ``` 30 /// # use googletest::prelude::*; 31 /// #[derive(Debug)] 32 /// struct IntField { 33 /// int: i32 34 /// } 35 /// # fn should_pass() -> Result<()> { 36 /// verify_that!(IntField{int: 32}, field!(IntField.int, eq(32)))?; 37 /// # Ok(()) 38 /// # } 39 /// # should_pass().unwrap(); 40 /// ``` 41 /// 42 /// Tuple structs are also supported via the index syntax: 43 /// 44 /// ``` 45 /// # use googletest::prelude::*; 46 /// #[derive(Debug)] 47 /// struct IntField(i32); 48 /// # fn should_pass() -> Result<()> { 49 /// verify_that!(IntField(32), field!(IntField.0, eq(32)))?; 50 /// # Ok(()) 51 /// # } 52 /// # should_pass().unwrap(); 53 /// ``` 54 /// 55 /// Enums are also supported, in which case only the specified variant is 56 /// matched: 57 /// 58 /// ``` 59 /// # use googletest::prelude::*; 60 /// #[derive(Debug)] 61 /// enum MyEnum { 62 /// A(i32), 63 /// B, 64 /// } 65 /// # fn should_pass() -> Result<()> { 66 /// verify_that!(MyEnum::A(32), field!(MyEnum::A.0, eq(32)))?; // Passes 67 /// # Ok(()) 68 /// # } 69 /// # fn should_fail() -> Result<()> { 70 /// verify_that!(MyEnum::B, field!(MyEnum::A.0, eq(32)))?; // Fails: wrong enum variant 71 /// # Ok(()) 72 /// # } 73 /// # should_pass().unwrap(); 74 /// # should_fail().unwrap_err(); 75 /// ``` 76 /// 77 /// The structure or enum may also be referenced from a separate module: 78 /// 79 /// ``` 80 /// # use googletest::prelude::*; 81 /// mod a_module { 82 /// #[derive(Debug)] 83 /// pub struct AStruct(pub i32); 84 /// } 85 /// # fn should_pass() -> Result<()> { 86 /// verify_that!(a_module::AStruct(32), field!(a_module::AStruct.0, eq(32)))?; 87 /// # Ok(()) 88 /// # } 89 /// # should_pass().unwrap(); 90 /// ``` 91 /// 92 /// Nested structures are *not supported*, however: 93 /// 94 /// ```compile_fail 95 /// # use googletest::prelude::*; 96 /// #[derive(Debug)] 97 /// struct InnerStruct(i32); 98 /// #[derive(Debug)] 99 /// struct OuterStruct { 100 /// inner: InnerStruct, 101 /// } 102 /// # fn should_not_compile() -> Result<()> { 103 /// verify_that!(value, field!(OuterStruct.inner.0, eq(32)))?; // Does not compile 104 /// # Ok(()) 105 /// # } 106 /// ``` 107 /// 108 /// See also the macro [`property`][crate::matchers::property] for an analogous 109 /// mechanism to extract a datum by invoking a method. 110 #[macro_export] 111 #[doc(hidden)] 112 macro_rules! __field { 113 ($($t:tt)*) => { $crate::field_internal!($($t)*) } 114 } 115 116 // Internal-only macro created so that the macro definition does not appear in 117 // generated documentation. 118 #[doc(hidden)] 119 #[macro_export] 120 macro_rules! field_internal { 121 ($($t:ident)::+.$field:tt, $m:expr) => {{ 122 use $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher; 123 field_matcher( 124 |o| { 125 match o { 126 $($t)::* { $field: value, .. } => Some(value), 127 // The pattern below is unreachable if the type is a struct (as opposed to an 128 // enum). Since the macro can't know which it is, we always include it and just 129 // tell the compiler not to complain. 130 #[allow(unreachable_patterns)] 131 _ => None, 132 } 133 }, 134 &stringify!($field), 135 $m) 136 }}; 137 } 138 139 /// Functions for use only by the declarative macros in this module. 140 /// 141 /// **For internal use only. API stablility is not guaranteed!** 142 #[doc(hidden)] 143 pub mod internal { 144 use crate::{ 145 description::Description, 146 matcher::{Matcher, MatcherResult}, 147 }; 148 use std::fmt::Debug; 149 150 /// Creates a matcher to verify a specific field of the actual struct using 151 /// the provided inner matcher. 152 /// 153 /// **For internal use only. API stablility is not guaranteed!** 154 #[doc(hidden)] field_matcher<OuterT: Debug, InnerT: Debug, InnerMatcher: Matcher<ActualT = InnerT>>( field_accessor: fn(&OuterT) -> Option<&InnerT>, field_path: &'static str, inner: InnerMatcher, ) -> impl Matcher<ActualT = OuterT>155 pub fn field_matcher<OuterT: Debug, InnerT: Debug, InnerMatcher: Matcher<ActualT = InnerT>>( 156 field_accessor: fn(&OuterT) -> Option<&InnerT>, 157 field_path: &'static str, 158 inner: InnerMatcher, 159 ) -> impl Matcher<ActualT = OuterT> { 160 FieldMatcher { field_accessor, field_path, inner } 161 } 162 163 struct FieldMatcher<OuterT, InnerT, InnerMatcher> { 164 field_accessor: fn(&OuterT) -> Option<&InnerT>, 165 field_path: &'static str, 166 inner: InnerMatcher, 167 } 168 169 impl<OuterT: Debug, InnerT: Debug, InnerMatcher: Matcher<ActualT = InnerT>> Matcher 170 for FieldMatcher<OuterT, InnerT, InnerMatcher> 171 { 172 type ActualT = OuterT; 173 matches(&self, actual: &OuterT) -> MatcherResult174 fn matches(&self, actual: &OuterT) -> MatcherResult { 175 if let Some(value) = (self.field_accessor)(actual) { 176 self.inner.matches(value) 177 } else { 178 MatcherResult::NoMatch 179 } 180 } 181 explain_match(&self, actual: &OuterT) -> Description182 fn explain_match(&self, actual: &OuterT) -> Description { 183 if let Some(actual) = (self.field_accessor)(actual) { 184 format!( 185 "which has field `{}`, {}", 186 self.field_path, 187 self.inner.explain_match(actual) 188 ) 189 .into() 190 } else { 191 let formatted_actual_value = format!("{actual:?}"); 192 let without_fields = formatted_actual_value.split('(').next().unwrap_or(""); 193 let without_fields = without_fields.split('{').next().unwrap_or("").trim_end(); 194 format!("which has the wrong enum variant `{without_fields}`").into() 195 } 196 } 197 describe(&self, matcher_result: MatcherResult) -> Description198 fn describe(&self, matcher_result: MatcherResult) -> Description { 199 format!( 200 "has field `{}`, which {}", 201 self.field_path, 202 self.inner.describe(matcher_result) 203 ) 204 .into() 205 } 206 } 207 } 208