Skip to content

Commit 366d2b3

Browse files
Merge pull request google#386 from google:pattern_matching
PiperOrigin-RevId: 631336719
2 parents 1f3ca57 + 5ebf234 commit 366d2b3

File tree

5 files changed

+199
-11
lines changed

5 files changed

+199
-11
lines changed

googletest/src/matchers/matches_pattern.rs

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -453,11 +453,10 @@ macro_rules! matches_pattern_internal {
453453
(
454454
[$($struct_name:tt)*],
455455
) => {
456-
$crate::matchers::predicate(|v| matches!(v, $($struct_name)*))
457-
.with_description(
458-
concat!("is ", stringify!($($struct_name)*)),
459-
concat!("is not ", stringify!($($struct_name)*)),
460-
)
456+
$crate::matchers::__internal_unstable_do_not_depend_on_these::pattern_only(
457+
|v| matches!(v, $($struct_name)*),
458+
concat!("is ", stringify!($($struct_name)*)),
459+
concat!("is not ", stringify!($($struct_name)*)))
461460
};
462461

463462
(
@@ -824,3 +823,59 @@ macro_rules! matches_pattern_internal {
824823
macro_rules! __pat {
825824
($($t:tt)*) => { $crate::matches_pattern_internal!($($t)*) }
826825
}
826+
827+
#[doc(hidden)]
828+
pub mod internal {
829+
use crate::matcher::{Matcher, MatcherBase};
830+
use std::fmt::Debug;
831+
832+
// Specialized implementation of the `predicate` matcher to support ref binding
833+
// mode for `matches_pattern`.
834+
pub fn pattern_only<T>(
835+
matcher_function: fn(&T) -> bool,
836+
match_description: &'static str,
837+
no_match_description: &'static str,
838+
) -> PatternOnlyMatcher<T> {
839+
PatternOnlyMatcher { matcher_function, match_description, no_match_description }
840+
}
841+
842+
#[derive(MatcherBase)]
843+
#[doc(hidden)]
844+
pub struct PatternOnlyMatcher<T> {
845+
matcher_function: fn(&T) -> bool,
846+
match_description: &'static str,
847+
no_match_description: &'static str,
848+
}
849+
850+
impl<'a, T: Debug> Matcher<&'a T> for PatternOnlyMatcher<T> {
851+
fn matches(&self, actual: &'a T) -> crate::matcher::MatcherResult {
852+
(self.matcher_function)(actual).into()
853+
}
854+
855+
fn describe(
856+
&self,
857+
matcher_result: crate::matcher::MatcherResult,
858+
) -> crate::description::Description {
859+
match matcher_result {
860+
crate::matcher::MatcherResult::Match => self.match_description.into(),
861+
crate::matcher::MatcherResult::NoMatch => self.no_match_description.into(),
862+
}
863+
}
864+
}
865+
866+
impl<T: Debug + Copy> Matcher<T> for PatternOnlyMatcher<T> {
867+
fn matches(&self, actual: T) -> crate::matcher::MatcherResult {
868+
(self.matcher_function)(&actual).into()
869+
}
870+
871+
fn describe(
872+
&self,
873+
matcher_result: crate::matcher::MatcherResult,
874+
) -> crate::description::Description {
875+
match matcher_result {
876+
crate::matcher::MatcherResult::Match => self.match_description.into(),
877+
crate::matcher::MatcherResult::NoMatch => self.no_match_description.into(),
878+
}
879+
}
880+
}
881+
}

googletest/src/matchers/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ pub mod __internal_unstable_do_not_depend_on_these {
110110
pub use super::elements_are_matcher::internal::ElementsAre;
111111
pub use super::field_matcher::internal::field_matcher;
112112
pub use super::is_matcher::is;
113+
pub use super::matches_pattern::internal::pattern_only;
113114
pub use super::pointwise_matcher::internal::PointwiseMatcher;
114115
pub use super::property_matcher::internal::{property_matcher, property_ref_matcher};
115116
pub use super::unordered_elements_are_matcher::internal::{

googletest/src/matchers/predicate_matcher.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,7 @@ use std::fmt::Debug;
3636
/// the closure argument, it is likely that it won't.
3737
/// See <https://github.com/rust-lang/rust/issues/12679> for update on this issue.
3838
/// This is easily fixed by explicitly declaring the type of the argument
39-
pub fn predicate<T: Debug + Copy, P>(
40-
predicate: P,
41-
) -> PredicateMatcher<P, NoDescription, NoDescription>
42-
where
43-
P: Fn(T) -> bool,
44-
{
39+
pub fn predicate<P>(predicate: P) -> PredicateMatcher<P, NoDescription, NoDescription> {
4540
PredicateMatcher {
4641
predicate,
4742
positive_description: NoDescription,

googletest/tests/composition_test.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,118 @@ fn elements_are_works_as_inner_matcher() -> Result<()> {
6969
fn tuple_works_as_inner_matcher() -> Result<()> {
7070
verify_that!(vec![(123,)], elements_are![(eq(&123),)])
7171
}
72+
73+
#[test]
74+
fn matches_struct_with_method_returning_option_of_non_copy_value() -> Result<()> {
75+
#[derive(Debug)]
76+
struct AnInnerStruct;
77+
78+
#[derive(Debug)]
79+
struct AStruct;
80+
81+
impl AStruct {
82+
fn get_value(&self) -> Option<AnInnerStruct> {
83+
Some(AnInnerStruct)
84+
}
85+
}
86+
87+
verify_that!(
88+
AStruct,
89+
matches_pattern!(&AStruct {
90+
get_value(): ref some(matches_pattern!(&AnInnerStruct))
91+
})
92+
)
93+
}
94+
95+
#[test]
96+
fn matches_struct_with_method_returning_option_of_non_copy_enum() -> Result<()> {
97+
#[derive(Debug)]
98+
enum AnInnerStruct {
99+
ThisCase,
100+
#[allow(unused)]
101+
ThatCase,
102+
}
103+
#[derive(Debug)]
104+
struct AStruct;
105+
impl AStruct {
106+
fn get_value(&self) -> Option<AnInnerStruct> {
107+
Some(AnInnerStruct::ThisCase)
108+
}
109+
}
110+
111+
verify_that!(
112+
AStruct,
113+
matches_pattern!(&AStruct {
114+
get_value(): ref some(matches_pattern!(&AnInnerStruct::ThisCase))
115+
})
116+
)
117+
}
118+
119+
#[test]
120+
fn matches_struct_with_method_returning_option_ref_binding_mode() -> Result<()> {
121+
#[derive(Debug)]
122+
struct AnInnerStruct;
123+
#[derive(Debug)]
124+
struct AStruct;
125+
impl AStruct {
126+
fn get_value(&self) -> Option<AnInnerStruct> {
127+
Some(AnInnerStruct)
128+
}
129+
}
130+
131+
verify_that!(
132+
AStruct,
133+
matches_pattern!(AStruct {
134+
get_value(): some(matches_pattern!(AnInnerStruct))
135+
})
136+
)
137+
}
138+
139+
#[test]
140+
fn matches_struct_with_method_returning_option_enum_ref_binding_mode() -> Result<()> {
141+
#[derive(Debug)]
142+
enum AnInnerStruct {
143+
ThisCase,
144+
#[allow(unused)]
145+
ThatCase,
146+
}
147+
#[derive(Debug)]
148+
struct AStruct;
149+
impl AStruct {
150+
fn get_value(&self) -> Option<AnInnerStruct> {
151+
Some(AnInnerStruct::ThisCase)
152+
}
153+
}
154+
155+
verify_that!(
156+
AStruct,
157+
matches_pattern!(AStruct {
158+
get_value(): some(matches_pattern!(AnInnerStruct::ThisCase))
159+
})
160+
)
161+
}
162+
163+
#[test]
164+
fn matches_struct_with_property_against_predicate() -> Result<()> {
165+
#[derive(Debug)]
166+
enum AnInnerStruct {
167+
ThisCase,
168+
#[allow(unused)]
169+
ThatCase,
170+
}
171+
172+
#[derive(Debug)]
173+
struct AStruct;
174+
impl AStruct {
175+
fn get_value(&self) -> AnInnerStruct {
176+
AnInnerStruct::ThisCase
177+
}
178+
}
179+
180+
verify_that!(
181+
AStruct,
182+
matches_pattern!(AStruct {
183+
get_value(): predicate(|_: &_| true)
184+
})
185+
)
186+
}

googletest/tests/matches_pattern_test.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,28 @@ fn matches_enum_without_field() -> Result<()> {
487487
verify_that!(actual, matches_pattern!(&AnEnum::A))
488488
}
489489

490+
#[test]
491+
fn matches_enum_without_field_ref_binding_mode() -> Result<()> {
492+
#[derive(Debug)]
493+
enum AnEnum {
494+
A,
495+
}
496+
let actual = AnEnum::A;
497+
498+
verify_that!(actual, matches_pattern!(AnEnum::A))
499+
}
500+
501+
#[test]
502+
fn matches_enum_without_field_copy() -> Result<()> {
503+
#[derive(Debug, Clone, Copy)]
504+
enum AnEnum {
505+
A,
506+
}
507+
let actual = AnEnum::A;
508+
509+
verify_that!(actual, matches_pattern!(AnEnum::A))
510+
}
511+
490512
#[test]
491513
fn generates_correct_failure_output_when_enum_variant_without_field_is_not_matched() -> Result<()> {
492514
#[derive(Debug)]

0 commit comments

Comments
 (0)