@@ -30,6 +30,51 @@ impl<T> Matcher<Option<T>> for SomeMatcher {
30
30
}
31
31
}
32
32
33
+ /// Matches if `actual` is an [`Option::Some`] *and* the contained value matches the inner matcher.
34
+ ///
35
+ /// [`Option::Some`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.Some
36
+ ///
37
+ /// # Examples
38
+ ///
39
+ /// ```
40
+ /// # use expect::{expect, matchers::{equal, collection::contain, option::match_some,
41
+ /// string::match_regex}};
42
+ /// expect(&Some(vec![1, 2, 3])).to(match_some(contain(2)));
43
+ /// expect(&Some("foo")).not_to(match_some(match_regex("\\d+")));
44
+ /// expect(&None::<&str>).not_to(match_some(equal("foo")));
45
+ /// ```
46
+ pub fn match_some < I > ( inner : I ) -> MatchSomeMatcher < I > {
47
+ MatchSomeMatcher { inner }
48
+ }
49
+
50
+ pub struct MatchSomeMatcher < I > {
51
+ inner : I ,
52
+ }
53
+
54
+ impl < T : std:: fmt:: Debug , M : Matcher < T > > Matcher < Option < T > > for MatchSomeMatcher < M > {
55
+ fn match_value ( & self , actual : & Option < T > ) -> bool {
56
+ if let Some ( value) = actual {
57
+ return self . inner . match_value ( value) ;
58
+ }
59
+ false
60
+ }
61
+
62
+ fn description ( & self , actual : & Option < T > ) -> Description {
63
+ if let Some ( value) = actual {
64
+ let inner_desc = self . inner . description ( value) ;
65
+ Description {
66
+ verb : format ! ( "be a Some and {}" , inner_desc. verb) ,
67
+ object : inner_desc. object ,
68
+ }
69
+ } else {
70
+ Description {
71
+ verb : String :: from ( "be a Some" ) ,
72
+ object : None ,
73
+ }
74
+ }
75
+ }
76
+ }
77
+
33
78
/// Matches if `actual` is an [`Option::None`].
34
79
///
35
80
/// [`Option::None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
@@ -62,8 +107,8 @@ impl<T> Matcher<Option<T>> for NoneMatcher {
62
107
63
108
#[ cfg( test) ]
64
109
mod tests {
65
- use super :: { be_none, be_some} ;
66
- use crate :: Matcher ;
110
+ use super :: { be_none, be_some, match_some } ;
111
+ use crate :: { matchers :: equal , Matcher } ;
67
112
68
113
#[ test]
69
114
fn some_matcher_should_match_if_actual_is_some ( ) {
@@ -98,4 +143,34 @@ mod tests {
98
143
assert_eq ! ( description. verb, String :: from( "be None" ) ) ;
99
144
assert_eq ! ( description. object, None ) ;
100
145
}
146
+
147
+ #[ test]
148
+ fn match_some_matcher_should_match_if_actual_is_some_and_inner_value_matches_inner_matcher ( ) {
149
+ assert ! ( match_some( equal( "foo" ) ) . match_value( & Some ( "foo" ) ) )
150
+ }
151
+
152
+ #[ test]
153
+ fn match_some_matcher_should_not_match_if_actual_is_some_but_inner_value_does_not_match_inner_matcher (
154
+ ) {
155
+ assert ! ( !match_some( equal( "foo" ) ) . match_value( & Some ( "bar" ) ) )
156
+ }
157
+
158
+ #[ test]
159
+ fn match_some_matcher_should_not_match_if_actual_is_not_some ( ) {
160
+ assert ! ( !match_some( equal( "foo" ) ) . match_value( & None :: <& str >) )
161
+ }
162
+
163
+ #[ test]
164
+ fn match_some_matcher_should_describe_itself_when_actual_is_not_some ( ) {
165
+ let description = match_some ( equal ( "foo" ) ) . description ( & None :: < & str > ) ;
166
+ assert_eq ! ( description. verb, String :: from( "be a Some" ) ) ;
167
+ assert_eq ! ( description. object, None )
168
+ }
169
+
170
+ #[ test]
171
+ fn match_some_matcher_should_describe_itself_and_its_inner_matcher_when_actual_is_some ( ) {
172
+ let description = match_some ( equal ( "foo" ) ) . description ( & Some ( "foo" ) ) ;
173
+ assert_eq ! ( description. verb, String :: from( "be a Some and equal" ) ) ;
174
+ assert_eq ! ( description. object, Some ( String :: from( "\" foo\" " ) ) )
175
+ }
101
176
}
0 commit comments