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 value according to a pattern of matchers. 20 /// 21 /// This takes as an argument a specification similar to a struct or enum 22 /// initialiser, where each value is a [`Matcher`][crate::matcher::Matcher] 23 /// which is applied to the corresponding field. 24 /// 25 /// This can be used to match arbitrary combinations of fields on structures 26 /// using arbitrary matchers: 27 /// 28 /// ``` 29 /// # use googletest::prelude::*; 30 /// #[derive(Debug)] 31 /// struct MyStruct { 32 /// a_field: String, 33 /// another_field: String, 34 /// } 35 /// 36 /// let my_struct = MyStruct { 37 /// a_field: "Something to believe in".into(), 38 /// another_field: "Something else".into() 39 /// }; 40 /// verify_that!(my_struct, matches_pattern!(MyStruct { 41 /// a_field: starts_with("Something"), 42 /// another_field: ends_with("else"), 43 /// })) 44 /// # .unwrap(); 45 /// ``` 46 /// 47 /// It is not required to include all named fields in the specification. Omitted 48 /// fields have no effect on the output of the matcher. 49 /// 50 /// ``` 51 /// # use googletest::prelude::*; 52 /// # #[derive(Debug)] 53 /// # struct MyStruct { 54 /// # a_field: String, 55 /// # another_field: String, 56 /// # } 57 /// # 58 /// # let my_struct = MyStruct { 59 /// # a_field: "Something to believe in".into(), 60 /// # another_field: "Something else".into() 61 /// # }; 62 /// verify_that!(my_struct, matches_pattern!(MyStruct { 63 /// a_field: starts_with("Something"), 64 /// // another_field is missing, so it may be anything. 65 /// })) 66 /// # .unwrap(); 67 /// ``` 68 /// 69 /// One can use it recursively to match nested structures: 70 /// 71 /// ``` 72 /// # use googletest::prelude::*; 73 /// #[derive(Debug)] 74 /// struct MyStruct { 75 /// a_nested_struct: MyInnerStruct, 76 /// } 77 /// 78 /// #[derive(Debug)] 79 /// struct MyInnerStruct { 80 /// a_field: String, 81 /// } 82 /// 83 /// let my_struct = MyStruct { 84 /// a_nested_struct: MyInnerStruct { a_field: "Something to believe in".into() }, 85 /// }; 86 /// verify_that!(my_struct, matches_pattern!(MyStruct { 87 /// a_nested_struct: matches_pattern!(MyInnerStruct { 88 /// a_field: starts_with("Something"), 89 /// }), 90 /// })) 91 /// # .unwrap(); 92 /// ``` 93 /// 94 /// One can use the alias [`pat`][crate::matchers::pat] to make this less 95 /// verbose: 96 /// 97 /// ``` 98 /// # use googletest::prelude::*; 99 /// # #[derive(Debug)] 100 /// # struct MyStruct { 101 /// # a_nested_struct: MyInnerStruct, 102 /// # } 103 /// # 104 /// # #[derive(Debug)] 105 /// # struct MyInnerStruct { 106 /// # a_field: String, 107 /// # } 108 /// # 109 /// # let my_struct = MyStruct { 110 /// # a_nested_struct: MyInnerStruct { a_field: "Something to believe in".into() }, 111 /// # }; 112 /// verify_that!(my_struct, matches_pattern!(MyStruct { 113 /// a_nested_struct: pat!(MyInnerStruct { 114 /// a_field: starts_with("Something"), 115 /// }), 116 /// })) 117 /// # .unwrap(); 118 /// ``` 119 /// 120 /// In addition to fields, one can match on the outputs of methods 121 /// ("properties"): 122 /// 123 /// ``` 124 /// # use googletest::prelude::*; 125 /// #[derive(Debug)] 126 /// struct MyStruct { 127 /// a_field: String, 128 /// } 129 /// 130 /// impl MyStruct { 131 /// fn get_a_field(&self) -> String { self.a_field.clone() } 132 /// } 133 /// 134 /// let my_struct = MyStruct { a_field: "Something to believe in".into() }; 135 /// verify_that!(my_struct, matches_pattern!(MyStruct { 136 /// get_a_field(): starts_with("Something"), 137 /// })) 138 /// # .unwrap(); 139 /// ``` 140 /// 141 /// **Important**: The method should be pure function with a deterministic 142 /// output and no side effects. In particular, in the event of an assertion 143 /// failure, it will be invoked a second time, with the assertion failure output 144 /// reflecting the *second* invocation. 145 /// 146 /// These may also include extra parameters you pass in: 147 /// 148 /// ``` 149 /// # use googletest::prelude::*; 150 /// # #[derive(Debug)] 151 /// # struct MyStruct { 152 /// # a_field: String, 153 /// # } 154 /// # 155 /// impl MyStruct { 156 /// fn append_to_a_field(&self, suffix: &str) -> String { self.a_field.clone() + suffix } 157 /// } 158 /// 159 /// # let my_struct = MyStruct { a_field: "Something to believe in".into() }; 160 /// verify_that!(my_struct, matches_pattern!(MyStruct { 161 /// append_to_a_field("a suffix"): ends_with("a suffix"), 162 /// })) 163 /// # .unwrap(); 164 /// ``` 165 /// 166 /// If the method returns a reference, precede it with a `*`: 167 /// 168 /// ``` 169 /// # use googletest::prelude::*; 170 /// # #[derive(Debug)] 171 /// # struct MyStruct { 172 /// # a_field: String, 173 /// # } 174 /// # 175 /// impl MyStruct { 176 /// fn get_a_field_ref(&self) -> &String { &self.a_field } 177 /// } 178 /// 179 /// # let my_struct = MyStruct { a_field: "Something to believe in".into() }; 180 /// verify_that!(my_struct, matches_pattern!(MyStruct { 181 /// *get_a_field_ref(): starts_with("Something"), 182 /// })) 183 /// # .unwrap(); 184 /// ``` 185 /// 186 /// One can also match tuple structs with up to 10 fields. In this case, all 187 /// fields must have matchers: 188 /// 189 /// ``` 190 /// # use googletest::prelude::*; 191 /// #[derive(Debug)] 192 /// struct MyTupleStruct(String, String); 193 /// 194 /// let my_struct = MyTupleStruct("Something".into(), "Some other thing".into()); 195 /// verify_that!( 196 /// my_struct, 197 /// matches_pattern!(MyTupleStruct(eq("Something"), eq("Some other thing"))) 198 /// ) 199 /// # .unwrap(); 200 /// ``` 201 /// 202 /// One can also match enum values: 203 /// 204 /// ``` 205 /// # use googletest::prelude::*; 206 /// #[derive(Debug)] 207 /// enum MyEnum { 208 /// A(u32), 209 /// B, 210 /// } 211 /// 212 /// # fn should_pass() -> Result<()> { 213 /// verify_that!(MyEnum::A(123), matches_pattern!(MyEnum::A(eq(123))))?; // Passes 214 /// # Ok(()) 215 /// # } 216 /// # fn should_fail() -> Result<()> { 217 /// verify_that!(MyEnum::B, matches_pattern!(MyEnum::A(eq(123))))?; // Fails - wrong enum variant 218 /// # Ok(()) 219 /// # } 220 /// # should_pass().unwrap(); 221 /// # should_fail().unwrap_err(); 222 /// ``` 223 /// 224 /// This macro does not support plain (non-struct) tuples. Use the macro 225 /// [`tuple`] for that purpose. 226 /// 227 /// Trailing commas are allowed (but not required) in both ordinary and tuple 228 /// structs. 229 /// 230 /// Unfortunately, this matcher does *not* work with methods returning string 231 /// slices: 232 /// 233 /// ```compile_fail 234 /// # use googletest::prelude::*; 235 /// # #[derive(Debug)] 236 /// pub struct MyStruct { 237 /// a_string: String, 238 /// } 239 /// impl MyStruct { 240 /// pub fn get_a_string(&self) -> &str { &self.a_string } 241 /// } 242 /// 243 /// let value = MyStruct { a_string: "A string".into() }; 244 /// verify_that!(value, matches_pattern!( MyStruct { 245 /// get_a_string(): eq("A string"), // Does not compile 246 /// })) 247 /// # .unwrap(); 248 /// ``` 249 #[macro_export] 250 #[doc(hidden)] 251 macro_rules! __matches_pattern { 252 ($($t:tt)*) => { $crate::matches_pattern_internal!($($t)*) } 253 } 254 255 // Internal-only macro created so that the macro definition does not appear in 256 // generated documentation. 257 #[doc(hidden)] 258 #[macro_export] 259 macro_rules! matches_pattern_internal { 260 ( 261 [$($struct_name:tt)*], 262 { $field_name:ident : $matcher:expr $(,)? } 263 ) => { 264 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 265 stringify!($($struct_name)*), 266 all!(field!($($struct_name)*.$field_name, $matcher)) 267 ) 268 }; 269 270 ( 271 [$($struct_name:tt)*], 272 { $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? } 273 ) => { 274 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 275 stringify!($($struct_name)*), 276 all!(property!($($struct_name)*.$property_name($($argument),*), $matcher)) 277 ) 278 }; 279 280 ( 281 [$($struct_name:tt)*], 282 { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? } 283 ) => { 284 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 285 stringify!($($struct_name)*), 286 all!(property!(* $($struct_name)*.$property_name($($argument),*), $matcher)) 287 ) 288 }; 289 290 ( 291 [$($struct_name:tt)*], 292 { $field_name:ident : $matcher:expr, $($rest:tt)* } 293 ) => { 294 $crate::matches_pattern_internal!( 295 all!(field!($($struct_name)*.$field_name, $matcher)), 296 [$($struct_name)*], 297 { $($rest)* } 298 ) 299 }; 300 301 ( 302 [$($struct_name:tt)*], 303 { $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* } 304 ) => { 305 $crate::matches_pattern_internal!( 306 all!(property!($($struct_name)*.$property_name($($argument),*), $matcher)), 307 [$($struct_name)*], 308 { $($rest)* } 309 ) 310 }; 311 312 ( 313 [$($struct_name:tt)*], 314 { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* } 315 ) => { 316 $crate::matches_pattern_internal!( 317 all!(property!(* $($struct_name)*.$property_name($($argument),*), $matcher)), 318 [$($struct_name)*], 319 { $($rest)* } 320 ) 321 }; 322 323 ( 324 all!($($processed:tt)*), 325 [$($struct_name:tt)*], 326 { $field_name:ident : $matcher:expr $(,)? } 327 ) => { 328 $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!( 329 $($processed)*, 330 field!($($struct_name)*.$field_name, $matcher) 331 )) 332 }; 333 334 ( 335 all!($($processed:tt)*), 336 [$($struct_name:tt)*], 337 { $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? } 338 ) => { 339 $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!( 340 $($processed)*, 341 property!($($struct_name)*.$property_name($($argument),*), $matcher) 342 )) 343 }; 344 345 ( 346 all!($($processed:tt)*), 347 [$($struct_name:tt)*], 348 { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? } 349 ) => { 350 $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!( 351 $($processed)*, 352 property!(* $($struct_name)*.$property_name($($argument),*), $matcher) 353 )) 354 }; 355 356 ( 357 all!($($processed:tt)*), 358 [$($struct_name:tt)*], 359 { $field_name:ident : $matcher:expr, $($rest:tt)* } 360 ) => { 361 $crate::matches_pattern_internal!( 362 all!( 363 $($processed)*, 364 field!($($struct_name)*.$field_name, $matcher) 365 ), 366 [$($struct_name)*], 367 { $($rest)* } 368 ) 369 }; 370 371 ( 372 all!($($processed:tt)*), 373 [$($struct_name:tt)*], 374 { $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* } 375 ) => { 376 $crate::matches_pattern_internal!( 377 all!( 378 $($processed)*, 379 property!($($struct_name)*.$property_name($($argument),*), $matcher) 380 ), 381 [$($struct_name)*], 382 { $($rest)* } 383 ) 384 }; 385 386 ( 387 all!($($processed:tt)*), 388 [$($struct_name:tt)*], 389 { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* } 390 ) => { 391 $crate::matches_pattern_internal!( 392 all!( 393 $($processed)*, 394 property!(* $($struct_name)*.$property_name($($argument),*), $matcher) 395 ), 396 [$($struct_name)*], 397 { $($rest)* } 398 ) 399 }; 400 401 ( 402 [$($struct_name:tt)*], 403 ) => { 404 $crate::matchers::predicate(|v| matches!(v, $($struct_name)*)) 405 .with_description( 406 concat!("is ", stringify!($($struct_name)*)), 407 concat!("is not ", stringify!($($struct_name)*)), 408 ) 409 }; 410 411 ( 412 [$($struct_name:tt)*], 413 ($matcher:expr $(,)?) 414 ) => { 415 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 416 stringify!($($struct_name)*), 417 all!(field!($($struct_name)*.0, $matcher)) 418 ) 419 }; 420 421 ( 422 [$($struct_name:tt)*], 423 ($matcher:expr, $($rest:tt)*) 424 ) => { 425 $crate::matches_pattern_internal!( 426 all!( 427 field!($($struct_name)*.0, $matcher) 428 ), 429 [$($struct_name)*], 430 1, 431 ($($rest)*) 432 ) 433 }; 434 435 ( 436 all!($($processed:tt)*), 437 [$($struct_name:tt)*], 438 $field:tt, 439 ($matcher:expr $(,)?) 440 ) => { 441 $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!( 442 $($processed)*, 443 field!($($struct_name)*.$field, $matcher) 444 )) 445 }; 446 447 // We need to repeat this once for every supported field position, unfortunately. There appears 448 // to be no way in declarative macros to compute $field + 1 and have the result evaluated to a 449 // token which can be used as a tuple index. 450 ( 451 all!($($processed:tt)*), 452 [$($struct_name:tt)*], 453 1, 454 ($matcher:expr, $($rest:tt)*) 455 ) => { 456 $crate::matches_pattern_internal!( 457 all!( 458 $($processed)*, 459 field!($($struct_name)*.1, $matcher) 460 ), 461 [$($struct_name)*], 462 2, 463 ($($rest)*) 464 ) 465 }; 466 467 ( 468 all!($($processed:tt)*), 469 [$($struct_name:tt)*], 470 2, 471 ($matcher:expr, $($rest:tt)*) 472 ) => { 473 $crate::matches_pattern_internal!( 474 all!( 475 $($processed)*, 476 field!($($struct_name)*.2, $matcher) 477 ), 478 [$($struct_name)*], 479 3, 480 ($($rest)*) 481 ) 482 }; 483 484 ( 485 all!($($processed:tt)*), 486 [$($struct_name:tt)*], 487 3, 488 ($matcher:expr, $($rest:tt)*) 489 ) => { 490 $crate::matches_pattern_internal!( 491 all!( 492 $($processed)*, 493 field!($($struct_name)*.3, $matcher) 494 ), 495 [$($struct_name)*], 496 4, 497 ($($rest)*) 498 ) 499 }; 500 501 ( 502 all!($($processed:tt)*), 503 [$($struct_name:tt)*], 504 4, 505 ($matcher:expr, $($rest:tt)*) 506 ) => { 507 $crate::matches_pattern_internal!( 508 all!( 509 $($processed)*, 510 field!($($struct_name)*.4, $matcher) 511 ), 512 [$($struct_name)*], 513 5, 514 ($($rest)*) 515 ) 516 }; 517 518 ( 519 all!($($processed:tt)*), 520 [$($struct_name:tt)*], 521 5, 522 ($matcher:expr, $($rest:tt)*) 523 ) => { 524 $crate::matches_pattern_internal!( 525 all!( 526 $($processed)*, 527 field!($($struct_name)*.5, $matcher) 528 ), 529 [$($struct_name)*], 530 6, 531 ($($rest)*) 532 ) 533 }; 534 535 ( 536 all!($($processed:tt)*), 537 [$($struct_name:tt)*], 538 6, 539 ($matcher:expr, $($rest:tt)*) 540 ) => { 541 $crate::matches_pattern_internal!( 542 all!( 543 $($processed)*, 544 field!($($struct_name)*.6, $matcher) 545 ), 546 [$($struct_name)*], 547 7, 548 ($($rest)*) 549 ) 550 }; 551 552 ( 553 all!($($processed:tt)*), 554 [$($struct_name:tt)*], 555 7, 556 ($matcher:expr, $($rest:tt)*) 557 ) => { 558 $crate::matches_pattern_internal!( 559 all!( 560 $($processed)*, 561 field!($($struct_name)*.7, $matcher) 562 ), 563 [$($struct_name)*], 564 8, 565 ($($rest)*) 566 ) 567 }; 568 569 ( 570 all!($($processed:tt)*), 571 [$($struct_name:tt)*], 572 8, 573 ($matcher:expr, $($rest:tt)*) 574 ) => { 575 $crate::matches_pattern_internal!( 576 all!( 577 $($processed)*, 578 field!($($struct_name)*.8, $matcher) 579 ), 580 [$($struct_name)*], 581 9, 582 ($($rest)*) 583 ) 584 }; 585 586 ([$($struct_name:tt)*], $first:tt $($rest:tt)*) => { 587 $crate::matches_pattern_internal!([$($struct_name)* $first], $($rest)*) 588 }; 589 590 ($first:tt $($rest:tt)*) => {{ 591 #[allow(unused)] 592 use $crate::matchers::{all, field, property}; 593 $crate::matches_pattern_internal!([$first], $($rest)*) 594 }}; 595 } 596 597 /// An alias for [`matches_pattern`][crate::matchers::matches_pattern!]. 598 #[macro_export] 599 #[doc(hidden)] 600 macro_rules! __pat { 601 ($($t:tt)*) => { $crate::matches_pattern_internal!($($t)*) } 602 } 603