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 /// If an inner matcher is `eq(...)`, it can be omitted: 142 /// 143 /// ``` 144 /// # use googletest::prelude::*; 145 /// #[derive(Debug)] 146 /// struct MyStruct { 147 /// a_field: String, 148 /// another_field: String, 149 /// } 150 /// 151 /// let my_struct = MyStruct { 152 /// a_field: "this".into(), 153 /// another_field: "that".into() 154 /// }; 155 /// verify_that!(my_struct, matches_pattern!(MyStruct { 156 /// a_field: "this", 157 /// another_field: "that", 158 /// })) 159 /// # .unwrap(); 160 /// ``` 161 /// 162 /// **Important**: The method should be pure function with a deterministic 163 /// output and no side effects. In particular, in the event of an assertion 164 /// failure, it will be invoked a second time, with the assertion failure output 165 /// reflecting the *second* invocation. 166 /// 167 /// These may also include extra litteral parameters you pass in: 168 /// 169 /// ``` 170 /// # use googletest::prelude::*; 171 /// # #[derive(Debug)] 172 /// # struct MyStruct { 173 /// # a_field: String, 174 /// # } 175 /// # 176 /// impl MyStruct { 177 /// fn append_to_a_field(&self, suffix: &str) -> String { self.a_field.clone() + suffix } 178 /// } 179 /// 180 /// # let my_struct = MyStruct { a_field: "Something to believe in".into() }; 181 /// verify_that!(my_struct, matches_pattern!(&MyStruct { 182 /// append_to_a_field("a suffix"): ref ends_with("a suffix"), 183 /// })) 184 /// # .unwrap(); 185 /// ``` 186 /// 187 /// You can precede both field and property matchers with a `ref` to match the 188 /// result by reference: 189 /// 190 /// ``` 191 /// # use googletest::prelude::*; 192 /// # #[derive(Debug)] 193 /// # struct MyStruct { 194 /// # a_field: String, 195 /// # } 196 /// # 197 /// impl MyStruct { 198 /// fn get_a_field_ref(&self) -> String { self.a_field.clone() } 199 /// } 200 /// 201 /// # let my_struct = MyStruct { a_field: "Something to believe in".into() }; 202 /// verify_that!(my_struct, matches_pattern!(&MyStruct { 203 /// get_a_field_ref(): ref starts_with("Something"), 204 /// })) 205 /// # .unwrap(); 206 /// ``` 207 /// 208 /// Note that if the `actual` is of type `&ActualT` and the pattern type is 209 /// `ActualT`, this is automatically performed. This behavior is similar to the 210 /// reference binding mode in pattern matching. 211 /// 212 /// ``` 213 /// # use googletest::prelude::*; 214 /// # #[derive(Debug)] 215 /// # struct MyStruct { 216 /// # a_field: String, 217 /// # } 218 /// # 219 /// impl MyStruct { 220 /// fn get_a_field_ref(&self) -> String { self.a_field.clone() } 221 /// } 222 /// 223 /// # let my_struct = MyStruct { a_field: "Something to believe in".into() }; 224 /// verify_that!(my_struct, matches_pattern!(MyStruct { 225 /// get_a_field_ref(): starts_with("Something"), 226 /// })) 227 /// # .unwrap(); 228 /// ``` 229 /// 230 /// One can also match tuple structs with up to 10 fields. In this case, all 231 /// fields must have matchers: 232 /// 233 /// ``` 234 /// # use googletest::prelude::*; 235 /// #[derive(Debug)] 236 /// struct MyTupleStruct(String, String); 237 /// 238 /// let my_struct = MyTupleStruct("Something".into(), "Some other thing".into()); 239 /// verify_that!( 240 /// my_struct, 241 /// matches_pattern!(&MyTupleStruct(ref eq("Something"), ref eq("Some other thing"))) 242 /// ) 243 /// # .unwrap(); 244 /// ``` 245 /// 246 /// One can also match enum values: 247 /// 248 /// ``` 249 /// # use googletest::prelude::*; 250 /// #[derive(Debug)] 251 /// enum MyEnum { 252 /// A(u32), 253 /// B, 254 /// } 255 /// 256 /// # fn should_pass() -> Result<()> { 257 /// verify_that!(MyEnum::A(123), matches_pattern!(&MyEnum::A(eq(123))))?; // Passes 258 /// # Ok(()) 259 /// # } 260 /// # fn should_fail() -> Result<()> { 261 /// verify_that!(MyEnum::B, matches_pattern!(&MyEnum::A(eq(123))))?; // Fails - wrong enum variant 262 /// # Ok(()) 263 /// # } 264 /// # should_pass().unwrap(); 265 /// # should_fail().unwrap_err(); 266 /// ``` 267 /// 268 /// This macro does not support plain (non-struct) tuples. But it should not be 269 /// necessary as tuple of matchers are matchers of tuple. In other words, if 270 /// `MatcherU: Matcher<U>` and `MatcherT: Matcher<T>`, then `(MatcherU, 271 /// MatcherT): Matcher<(U, T)>`. 272 /// 273 /// Trailing commas are allowed (but not required) in both ordinary and tuple 274 /// structs. 275 /// 276 /// Note that the default format (rustfmt) can format macros if the macro 277 /// argument is parseable Rust code. This is mostly true for this macro with two 278 /// exceptions: 279 /// * property matching 280 /// * `ref` keyword with named fields 281 /// 282 /// An option for formatting large is to avoid these exceptions (by removing the 283 /// parenthesis of properties and the `ref` keywords), run `rustfmt` and add 284 /// them back. 285 #[macro_export] 286 #[doc(hidden)] 287 macro_rules! __matches_pattern { 288 ($($t:tt)*) => { $crate::matches_pattern_internal!($($t)*) } 289 } 290 291 // Internal-only macro created so that the macro definition does not appear in 292 // generated documentation. 293 #[doc(hidden)] 294 #[macro_export] 295 macro_rules! matches_pattern_internal { 296 ( 297 @name [$($struct_name:tt)*], 298 { $field_name:ident : ref $matcher:expr $(,)? } 299 ) => { 300 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 301 stringify!($($struct_name)*), 302 all!(field!($($struct_name)*.$field_name, ref $matcher)) 303 ) 304 }; 305 306 ( 307 @name [$($struct_name:tt)*], 308 { $field_name:ident : $matcher:expr $(,)? } 309 ) => { 310 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 311 stringify!($($struct_name)*), 312 all!(field!($($struct_name)*.$field_name, $matcher)) 313 ) 314 }; 315 316 ( 317 @name [$($struct_name:tt)*], 318 { $property_name:ident($($argument:expr),* $(,)?) : ref $matcher:expr $(,)? } 319 ) => { 320 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 321 stringify!($($struct_name)*), 322 all!(property!($($struct_name)*.$property_name($($argument),*), ref $matcher)) 323 ) 324 }; 325 326 ( 327 @name [$($struct_name:tt)*], 328 { $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? } 329 ) => { 330 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 331 stringify!($($struct_name)*), 332 all!(property!($($struct_name)*.$property_name($($argument),*), $matcher)) 333 ) 334 }; 335 336 ( 337 @name [$($struct_name:tt)*], 338 { $field_name:ident : ref $matcher:expr, $($rest:tt)* } 339 ) => { 340 $crate::matches_pattern_internal!( 341 @fields (field!($($struct_name)*.$field_name, ref $matcher)), 342 [$($struct_name)*], 343 { $($rest)* } 344 ) 345 }; 346 347 ( 348 @name [$($struct_name:tt)*], 349 { $field_name:ident : $matcher:expr, $($rest:tt)* } 350 ) => { 351 $crate::matches_pattern_internal!( 352 @fields (field!($($struct_name)*.$field_name, $matcher)), 353 [$($struct_name)*], 354 { $($rest)* } 355 ) 356 }; 357 358 ( 359 @name [$($struct_name:tt)*], 360 { $property_name:ident($($argument:expr),* $(,)?) : ref $matcher:expr, $($rest:tt)* } 361 ) => { 362 $crate::matches_pattern_internal!( 363 @fields (property!($($struct_name)*.$property_name($($argument),*), ref $matcher)), 364 [$($struct_name)*], 365 { $($rest)* } 366 ) 367 }; 368 369 ( 370 @name [$($struct_name:tt)*], 371 { $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* } 372 ) => { 373 $crate::matches_pattern_internal!( 374 @fields (property!($($struct_name)*.$property_name($($argument),*), $matcher)), 375 [$($struct_name)*], 376 { $($rest)* } 377 ) 378 }; 379 380 ( 381 @fields ($($processed:tt)*), 382 [$($struct_name:tt)*], 383 { $field_name:ident : ref $matcher:expr $(,)? } 384 ) => { 385 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 386 stringify!($($struct_name)*), 387 all!( 388 $($processed)*, 389 field!($($struct_name)*.$field_name, ref $matcher) 390 )) 391 }; 392 393 ( 394 @fields ($($processed:tt)*), 395 [$($struct_name:tt)*], 396 { $field_name:ident : $matcher:expr $(,)? } 397 ) => { 398 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 399 stringify!($($struct_name)*), 400 all!( 401 $($processed)*, 402 field!($($struct_name)*.$field_name, $matcher) 403 )) 404 }; 405 406 ( 407 @fields ($($processed:tt)*), 408 [$($struct_name:tt)*], 409 { $property_name:ident($($argument:expr),* $(,)?) : ref $matcher:expr $(,)? } 410 ) => { 411 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 412 stringify!($($struct_name)*), 413 all!( 414 $($processed)*, 415 property!($($struct_name)*.$property_name($($argument),*), ref $matcher) 416 )) 417 }; 418 419 ( 420 @fields ($($processed:tt)*), 421 [$($struct_name:tt)*], 422 { $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? } 423 ) => { 424 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 425 stringify!($($struct_name)*), 426 all!( 427 $($processed)*, 428 property!($($struct_name)*.$property_name($($argument),*), $matcher) 429 )) 430 }; 431 432 ( 433 @fields ($($processed:tt)*), 434 [$($struct_name:tt)*], 435 { $field_name:ident : ref $matcher:expr, $($rest:tt)* } 436 ) => { 437 $crate::matches_pattern_internal!( 438 @fields ( 439 $($processed)*, 440 field!($($struct_name)*.$field_name, ref $matcher) 441 ), 442 [$($struct_name)*], 443 { $($rest)* } 444 ) 445 }; 446 447 ( 448 @fields ($($processed:tt)*), 449 [$($struct_name:tt)*], 450 { $field_name:ident : $matcher:expr, $($rest:tt)* } 451 ) => { 452 $crate::matches_pattern_internal!( 453 @fields ( 454 $($processed)*, 455 field!($($struct_name)*.$field_name, $matcher) 456 ), 457 [$($struct_name)*], 458 { $($rest)* } 459 ) 460 }; 461 462 ( 463 @fields ($($processed:tt)*), 464 [$($struct_name:tt)*], 465 { $property_name:ident($($argument:expr),* $(,)?) : ref $matcher:expr, $($rest:tt)* } 466 ) => { 467 $crate::matches_pattern_internal!( 468 @fields ( 469 $($processed)*, 470 property!(ref $($struct_name)*.$property_name($($argument),*), $matcher) 471 ), 472 [$($struct_name)*], 473 { $($rest)* } 474 ) 475 }; 476 477 ( 478 @fields ($($processed:tt)*), 479 [$($struct_name:tt)*], 480 { $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* } 481 ) => { 482 $crate::matches_pattern_internal!( 483 @fields ( 484 $($processed)*, 485 property!($($struct_name)*.$property_name($($argument),*), $matcher) 486 ), 487 [$($struct_name)*], 488 { $($rest)* } 489 ) 490 }; 491 492 ( 493 @name [$($struct_name:tt)*], 494 ) => { 495 $crate::matchers::__internal_unstable_do_not_depend_on_these::pattern_only( 496 |v| matches!(v, $($struct_name)*), 497 concat!("is ", stringify!($($struct_name)*)), 498 concat!("is not ", stringify!($($struct_name)*))) 499 }; 500 501 ( 502 @name [$($struct_name:tt)*], 503 (ref $matcher:expr $(,)?) 504 ) => { 505 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 506 stringify!($($struct_name)*), 507 all!(field!($($struct_name)*.0, ref $matcher)) 508 ) 509 }; 510 511 ( 512 @name [$($struct_name:tt)*], 513 ($matcher:expr $(,)?) 514 ) => { 515 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 516 stringify!($($struct_name)*), 517 all!(field!($($struct_name)*.0, $matcher)) 518 ) 519 }; 520 521 ( 522 @name [$($struct_name:tt)*], 523 (ref $matcher:expr, $($rest:tt)*) 524 ) => { 525 $crate::matches_pattern_internal!( 526 @fields ( 527 field!($($struct_name)*.0, ref $matcher) 528 ), 529 [$($struct_name)*], 530 1, 531 ($($rest)*) 532 ) 533 }; 534 535 ( 536 @name [$($struct_name:tt)*], 537 ($matcher:expr, $($rest:tt)*) 538 ) => { 539 $crate::matches_pattern_internal!( 540 @fields ( 541 field!($($struct_name)*.0, $matcher) 542 ), 543 [$($struct_name)*], 544 1, 545 ($($rest)*) 546 ) 547 }; 548 549 ( 550 @fields ($($processed:tt)*), 551 [$($struct_name:tt)*], 552 $field:tt, 553 (ref $matcher:expr $(,)?) 554 ) => { 555 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 556 stringify!($($struct_name)*), 557 all!( 558 $($processed)*, 559 field!($($struct_name)*.$field, ref $matcher) 560 )) 561 }; 562 563 ( 564 @fields ($($processed:tt)*), 565 [$($struct_name:tt)*], 566 $field:tt, 567 ($matcher:expr $(,)?) 568 ) => { 569 $crate::matchers::__internal_unstable_do_not_depend_on_these::is( 570 stringify!($($struct_name)*), 571 all!( 572 $($processed)*, 573 field!($($struct_name)*.$field, $matcher) 574 )) 575 }; 576 577 // We need to repeat this once for every supported field position, unfortunately. There appears 578 // to be no way in declarative macros to compute $field + 1 and have the result evaluated to a 579 // token which can be used as a tuple index. 580 ( 581 @fields ($($processed:tt)*), 582 [$($struct_name:tt)*], 583 1, 584 (ref $matcher:expr, $($rest:tt)*) 585 ) => { 586 $crate::matches_pattern_internal!( 587 @fields ( 588 $($processed)*, 589 field!($($struct_name)*.1, ref $matcher) 590 ), 591 [$($struct_name)*], 592 2, 593 ($($rest)*) 594 ) 595 }; 596 597 ( 598 @fields ($($processed:tt)*), 599 [$($struct_name:tt)*], 600 1, 601 ($matcher:expr, $($rest:tt)*) 602 ) => { 603 $crate::matches_pattern_internal!( 604 @fields ( 605 $($processed)*, 606 field!($($struct_name)*.1, $matcher) 607 ), 608 [$($struct_name)*], 609 2, 610 ($($rest)*) 611 ) 612 }; 613 614 ( 615 @fields ($($processed:tt)*), 616 [$($struct_name:tt)*], 617 2, 618 (ref $matcher:expr, $($rest:tt)*) 619 ) => { 620 $crate::matches_pattern_internal!( 621 @fields ( 622 $($processed)*, 623 field!($($struct_name)*.2, ref $matcher) 624 ), 625 [$($struct_name)*], 626 3, 627 ($($rest)*) 628 ) 629 }; 630 631 ( 632 @fields ($($processed:tt)*), 633 [$($struct_name:tt)*], 634 2, 635 ($matcher:expr, $($rest:tt)*) 636 ) => { 637 $crate::matches_pattern_internal!( 638 @fields ( 639 $($processed)*, 640 field!($($struct_name)*.2, $matcher) 641 ), 642 [$($struct_name)*], 643 3, 644 ($($rest)*) 645 ) 646 }; 647 648 ( 649 @fields ($($processed:tt)*), 650 [$($struct_name:tt)*], 651 3, 652 (ref $matcher:expr, $($rest:tt)*) 653 ) => { 654 $crate::matches_pattern_internal!( 655 @fields ( 656 $($processed)*, 657 field!($($struct_name)*.3, ref $matcher) 658 ), 659 [$($struct_name)*], 660 4, 661 ($($rest)*) 662 ) 663 }; 664 665 ( 666 @fields ($($processed:tt)*), 667 [$($struct_name:tt)*], 668 3, 669 ($matcher:expr, $($rest:tt)*) 670 ) => { 671 $crate::matches_pattern_internal!( 672 @fields ( 673 $($processed)*, 674 field!($($struct_name)*.3, $matcher) 675 ), 676 [$($struct_name)*], 677 4, 678 ($($rest)*) 679 ) 680 }; 681 682 ( 683 @fields ($($processed:tt)*), 684 [$($struct_name:tt)*], 685 4, 686 (ref $matcher:expr, $($rest:tt)*) 687 ) => { 688 $crate::matches_pattern_internal!( 689 @fields ( 690 $($processed)*, 691 field!($($struct_name)*.4, ref $matcher) 692 ), 693 [$($struct_name)*], 694 5, 695 ($($rest)*) 696 ) 697 }; 698 699 ( 700 @fields ($($processed:tt)*), 701 [$($struct_name:tt)*], 702 4, 703 ($matcher:expr, $($rest:tt)*) 704 ) => { 705 $crate::matches_pattern_internal!( 706 @fields ( 707 $($processed)*, 708 field!($($struct_name)*.4, $matcher) 709 ), 710 [$($struct_name)*], 711 5, 712 ($($rest)*) 713 ) 714 }; 715 716 ( 717 @fields ($($processed:tt)*), 718 [$($struct_name:tt)*], 719 5, 720 (ref $matcher:expr, $($rest:tt)*) 721 ) => { 722 $crate::matches_pattern_internal!( 723 @fields ( 724 $($processed)*, 725 field!($($struct_name)*.5, ref $matcher) 726 ), 727 [$($struct_name)*], 728 6, 729 ($($rest)*) 730 ) 731 }; 732 733 ( 734 @fields ($($processed:tt)*), 735 [$($struct_name:tt)*], 736 5, 737 ($matcher:expr, $($rest:tt)*) 738 ) => { 739 $crate::matches_pattern_internal!( 740 @fields ( 741 $($processed)*, 742 field!($($struct_name)*.5, $matcher) 743 ), 744 [$($struct_name)*], 745 6, 746 ($($rest)*) 747 ) 748 }; 749 750 ( 751 @fields ($($processed:tt)*), 752 [$($struct_name:tt)*], 753 6, 754 (ref $matcher:expr, $($rest:tt)*) 755 ) => { 756 $crate::matches_pattern_internal!( 757 @fields ( 758 $($processed)*, 759 field!($($struct_name)*.6, ref $matcher) 760 ), 761 [$($struct_name)*], 762 7, 763 ($($rest)*) 764 ) 765 }; 766 767 ( 768 @fields ($($processed:tt)*), 769 [$($struct_name:tt)*], 770 6, 771 ($matcher:expr, $($rest:tt)*) 772 ) => { 773 $crate::matches_pattern_internal!( 774 @fields ( 775 $($processed)*, 776 field!($($struct_name)*.6, $matcher) 777 ), 778 [$($struct_name)*], 779 7, 780 ($($rest)*) 781 ) 782 }; 783 784 ( 785 @fields ($($processed:tt)*), 786 [$($struct_name:tt)*], 787 7, 788 (ref $matcher:expr, $($rest:tt)*) 789 ) => { 790 $crate::matches_pattern_internal!( 791 @fields ( 792 $($processed)*, 793 field!($($struct_name)*.7, ref $matcher) 794 ), 795 [$($struct_name)*], 796 8, 797 ($($rest)*) 798 ) 799 }; 800 801 ( 802 @fields ($($processed:tt)*), 803 [$($struct_name:tt)*], 804 7, 805 ($matcher:expr, $($rest:tt)*) 806 ) => { 807 $crate::matches_pattern_internal!( 808 @fields ( 809 $($processed)*, 810 field!($($struct_name)*.7, $matcher) 811 ), 812 [$($struct_name)*], 813 8, 814 ($($rest)*) 815 ) 816 }; 817 818 ( 819 @fields ($($processed:tt)*), 820 [$($struct_name:tt)*], 821 8, 822 (ref $matcher:expr, $($rest:tt)*) 823 ) => { 824 $crate::matches_pattern_internal!( 825 @fields ( 826 $($processed)*, 827 field!($($struct_name)*.8, ref $matcher) 828 ), 829 [$($struct_name)*], 830 9, 831 ($($rest)*) 832 ) 833 }; 834 835 ( 836 @fields ($($processed:tt)*), 837 [$($struct_name:tt)*], 838 8, 839 ($matcher:expr, $($rest:tt)*) 840 ) => { 841 $crate::matches_pattern_internal!( 842 @fields ( 843 $($processed)*, 844 field!($($struct_name)*.8, $matcher) 845 ), 846 [$($struct_name)*], 847 9, 848 ($($rest)*) 849 ) 850 }; 851 852 (@name [$($struct_name:tt)*], $first:tt $($rest:tt)*) => { 853 $crate::matches_pattern_internal!(@name [$($struct_name)* $first], $($rest)*) 854 }; 855 856 ($first:tt $($rest:tt)*) => {{ 857 #[allow(unused)] 858 use $crate::matchers::{all, field, property}; 859 $crate::matches_pattern_internal!(@name [$first], $($rest)*) 860 }}; 861 } 862 863 /// An alias for [`matches_pattern`][crate::matchers::matches_pattern!]. 864 #[macro_export] 865 #[doc(hidden)] 866 macro_rules! __pat { 867 ($($t:tt)*) => { $crate::matches_pattern_internal!($($t)*) } 868 } 869 870 #[doc(hidden)] 871 pub mod internal { 872 use crate::matcher::{Matcher, MatcherBase}; 873 use std::fmt::Debug; 874 875 // Specialized implementation of the `predicate` matcher to support ref binding 876 // mode for `matches_pattern`. pattern_only<T>( matcher_function: fn(&T) -> bool, match_description: &'static str, no_match_description: &'static str, ) -> PatternOnlyMatcher<T>877 pub fn pattern_only<T>( 878 matcher_function: fn(&T) -> bool, 879 match_description: &'static str, 880 no_match_description: &'static str, 881 ) -> PatternOnlyMatcher<T> { 882 PatternOnlyMatcher { matcher_function, match_description, no_match_description } 883 } 884 885 #[derive(MatcherBase)] 886 #[doc(hidden)] 887 pub struct PatternOnlyMatcher<T> { 888 matcher_function: fn(&T) -> bool, 889 match_description: &'static str, 890 no_match_description: &'static str, 891 } 892 893 impl<'a, T: Debug> Matcher<&'a T> for PatternOnlyMatcher<T> { matches(&self, actual: &'a T) -> crate::matcher::MatcherResult894 fn matches(&self, actual: &'a T) -> crate::matcher::MatcherResult { 895 (self.matcher_function)(actual).into() 896 } 897 describe( &self, matcher_result: crate::matcher::MatcherResult, ) -> crate::description::Description898 fn describe( 899 &self, 900 matcher_result: crate::matcher::MatcherResult, 901 ) -> crate::description::Description { 902 match matcher_result { 903 crate::matcher::MatcherResult::Match => self.match_description.into(), 904 crate::matcher::MatcherResult::NoMatch => self.no_match_description.into(), 905 } 906 } 907 } 908 909 impl<T: Debug + Copy> Matcher<T> for PatternOnlyMatcher<T> { matches(&self, actual: T) -> crate::matcher::MatcherResult910 fn matches(&self, actual: T) -> crate::matcher::MatcherResult { 911 (self.matcher_function)(&actual).into() 912 } 913 describe( &self, matcher_result: crate::matcher::MatcherResult, ) -> crate::description::Description914 fn describe( 915 &self, 916 matcher_result: crate::matcher::MatcherResult, 917 ) -> crate::description::Description { 918 match matcher_result { 919 crate::matcher::MatcherResult::Match => self.match_description.into(), 920 crate::matcher::MatcherResult::NoMatch => self.no_match_description.into(), 921 } 922 } 923 } 924 } 925