googletest/matchers/
predicate_matcher.rs1use crate::{
16 description::Description,
17 matcher::{Matcher, MatcherBase, MatcherResult},
18};
19use std::fmt::Debug;
20
21pub fn predicate<P>(predicate: P) -> PredicateMatcher<P, NoDescription, NoDescription> {
40 PredicateMatcher {
41 predicate,
42 positive_description: NoDescription,
43 negative_description: NoDescription,
44 }
45}
46
47impl<P> PredicateMatcher<P, NoDescription, NoDescription> {
48 pub fn with_description<D1: PredicateDescription, D2: PredicateDescription>(
64 self,
65 positive_description: D1,
66 negative_description: D2,
67 ) -> PredicateMatcher<P, D1, D2> {
68 PredicateMatcher { predicate: self.predicate, positive_description, negative_description }
69 }
70}
71
72#[derive(MatcherBase)]
76pub struct PredicateMatcher<P, D1, D2> {
77 predicate: P,
78 positive_description: D1,
79 negative_description: D2,
80}
81
82pub trait PredicateDescription {
87 fn to_description(&self) -> Description;
88}
89
90impl PredicateDescription for &str {
91 fn to_description(&self) -> Description {
92 self.to_string().into()
93 }
94}
95
96impl PredicateDescription for String {
97 fn to_description(&self) -> Description {
98 self.to_string().into()
99 }
100}
101
102impl<T, S> PredicateDescription for T
103where
104 T: Fn() -> S,
105 S: Into<String>,
106{
107 fn to_description(&self) -> Description {
108 self().into().into()
109 }
110}
111
112#[doc(hidden)]
114pub struct NoDescription;
115
116impl<T: Debug + Copy, P> Matcher<T> for PredicateMatcher<P, NoDescription, NoDescription>
117where
118 P: Fn(T) -> bool,
119{
120 fn matches(&self, actual: T) -> MatcherResult {
121 (self.predicate)(actual).into()
122 }
123
124 fn describe(&self, result: MatcherResult) -> Description {
125 match result {
126 MatcherResult::Match => "matches".into(),
127 MatcherResult::NoMatch => "does not match".into(),
128 }
129 }
130}
131
132impl<T: Debug + Copy, P, D1: PredicateDescription, D2: PredicateDescription> Matcher<T>
133 for PredicateMatcher<P, D1, D2>
134where
135 P: Fn(T) -> bool,
136{
137 fn matches(&self, actual: T) -> MatcherResult {
138 (self.predicate)(actual).into()
139 }
140
141 fn describe(&self, result: MatcherResult) -> Description {
142 match result {
143 MatcherResult::Match => self.positive_description.to_description(),
144 MatcherResult::NoMatch => self.negative_description.to_description(),
145 }
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use crate::prelude::*;
152 use crate::Result;
153
154 fn is_odd() -> impl Matcher<i32> {
156 predicate(|x| x % 2 == 1).with_description("is odd", "is even")
157 }
158
159 #[test]
160 fn predicate_matcher_odd() -> Result<()> {
161 verify_that!(1, is_odd())
162 }
163
164 #[test]
165 fn predicate_matcher_odd_explain_match_matches() -> Result<()> {
166 verify_that!(is_odd().explain_match(1), displays_as(eq("which is odd")))
167 }
168
169 #[test]
170 fn predicate_matcher_odd_explain_match_does_not_match() -> Result<()> {
171 verify_that!(is_odd().explain_match(2), displays_as(eq("which is even")))
172 }
173
174 fn is_even() -> impl Matcher<i32> {
176 predicate(|x| x % 2 == 0)
177 }
178
179 #[test]
180 fn predicate_matcher_even() -> Result<()> {
181 verify_that!(2, is_even())
182 }
183
184 #[test]
185 fn predicate_matcher_even_explain_match_matches() -> Result<()> {
186 verify_that!(is_even().explain_match(2), displays_as(eq("which matches")))
187 }
188
189 #[test]
190 fn predicate_matcher_even_explain_match_does_not_match() -> Result<()> {
191 verify_that!(is_even().explain_match(1), displays_as(eq("which does not match")))
192 }
193
194 #[test]
195 fn predicate_matcher_generator_lambda() -> Result<()> {
196 let is_divisible_by = |quotient| {
197 predicate(move |x: i32| x % quotient == 0).with_description(
198 move || format!("is divisible by {quotient}"),
199 move || format!("is not divisible by {quotient}"),
200 )
201 };
202 verify_that!(49, is_divisible_by(7))
203 }
204
205 #[test]
206 fn predicate_matcher_inline() -> Result<()> {
207 verify_that!(2048, predicate(|x: i32| x.count_ones() == 1))
208 }
209
210 #[test]
211 fn predicate_matcher_function_pointer() -> Result<()> {
212 use std::time::Duration;
213 verify_that!(&Duration::new(0, 0), predicate(Duration::is_zero))
214 }
215}