1use std::fmt;
10
11use crate::reflection;
12use crate::utils;
13use crate::Predicate;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct IsEmptyPredicate {}
20
21impl Predicate<str> for IsEmptyPredicate {
22    fn eval(&self, variable: &str) -> bool {
23        variable.is_empty()
24    }
25
26    fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option<reflection::Case<'a>> {
27        utils::default_find_case(self, expected, variable)
28            .map(|case| case.add_product(reflection::Product::new("var", variable.to_owned())))
29    }
30}
31
32impl reflection::PredicateReflection for IsEmptyPredicate {}
33
34impl fmt::Display for IsEmptyPredicate {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        let palette = crate::Palette::new(f.alternate());
37        write!(
38            f,
39            "{}.{}()",
40            palette.var("var"),
41            palette.description("is_empty"),
42        )
43    }
44}
45
46pub fn is_empty() -> IsEmptyPredicate {
58    IsEmptyPredicate {}
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
65pub struct StartsWithPredicate {
66    pattern: String,
67}
68
69impl Predicate<str> for StartsWithPredicate {
70    fn eval(&self, variable: &str) -> bool {
71        variable.starts_with(&self.pattern)
72    }
73
74    fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option<reflection::Case<'a>> {
75        utils::default_find_case(self, expected, variable)
76            .map(|case| case.add_product(reflection::Product::new("var", variable.to_owned())))
77    }
78}
79
80impl reflection::PredicateReflection for StartsWithPredicate {}
81
82impl fmt::Display for StartsWithPredicate {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        let palette = crate::Palette::new(f.alternate());
85        write!(
86            f,
87            "{}.{}({:?})",
88            palette.var("var"),
89            palette.description("starts_with"),
90            self.pattern
91        )
92    }
93}
94
95pub fn starts_with<P>(pattern: P) -> StartsWithPredicate
107where
108    P: Into<String>,
109{
110    StartsWithPredicate {
111        pattern: pattern.into(),
112    }
113}
114
115#[derive(Debug, Clone, PartialEq, Eq)]
119pub struct EndsWithPredicate {
120    pattern: String,
121}
122
123impl Predicate<str> for EndsWithPredicate {
124    fn eval(&self, variable: &str) -> bool {
125        variable.ends_with(&self.pattern)
126    }
127
128    fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option<reflection::Case<'a>> {
129        utils::default_find_case(self, expected, variable)
130            .map(|case| case.add_product(reflection::Product::new("var", variable.to_owned())))
131    }
132}
133
134impl reflection::PredicateReflection for EndsWithPredicate {}
135
136impl fmt::Display for EndsWithPredicate {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        let palette = crate::Palette::new(f.alternate());
139        write!(
140            f,
141            "{}.{}({:?})",
142            palette.var("var"),
143            palette.description("ends_with"),
144            self.pattern
145        )
146    }
147}
148
149pub fn ends_with<P>(pattern: P) -> EndsWithPredicate
161where
162    P: Into<String>,
163{
164    EndsWithPredicate {
165        pattern: pattern.into(),
166    }
167}
168
169#[derive(Debug, Clone, PartialEq, Eq)]
173pub struct ContainsPredicate {
174    pattern: String,
175}
176
177impl ContainsPredicate {
178    pub fn count(self, count: usize) -> MatchesPredicate {
190        MatchesPredicate {
191            pattern: self.pattern,
192            count,
193        }
194    }
195}
196
197impl Predicate<str> for ContainsPredicate {
198    fn eval(&self, variable: &str) -> bool {
199        variable.contains(&self.pattern)
200    }
201
202    fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option<reflection::Case<'a>> {
203        utils::default_find_case(self, expected, variable)
204            .map(|case| case.add_product(reflection::Product::new("var", variable.to_owned())))
205    }
206}
207
208impl reflection::PredicateReflection for ContainsPredicate {}
209
210impl fmt::Display for ContainsPredicate {
211    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212        let palette = crate::Palette::new(f.alternate());
213        write!(
214            f,
215            "{}.{}({})",
216            palette.var("var"),
217            palette.description("contains"),
218            palette.expected(&self.pattern),
219        )
220    }
221}
222
223#[derive(Debug, Clone, PartialEq, Eq)]
227pub struct MatchesPredicate {
228    pattern: String,
229    count: usize,
230}
231
232impl Predicate<str> for MatchesPredicate {
233    fn eval(&self, variable: &str) -> bool {
234        variable.matches(&self.pattern).count() == self.count
235    }
236
237    fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option<reflection::Case<'a>> {
238        let actual_count = variable.matches(&self.pattern).count();
239        let result = self.count == actual_count;
240        if result == expected {
241            Some(
242                reflection::Case::new(Some(self), result)
243                    .add_product(reflection::Product::new("var", variable.to_owned()))
244                    .add_product(reflection::Product::new("actual count", actual_count)),
245            )
246        } else {
247            None
248        }
249    }
250}
251
252impl reflection::PredicateReflection for MatchesPredicate {
253    fn parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a> {
254        let params = vec![reflection::Parameter::new("count", &self.count)];
255        Box::new(params.into_iter())
256    }
257}
258
259impl fmt::Display for MatchesPredicate {
260    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261        let palette = crate::Palette::new(f.alternate());
262        write!(
263            f,
264            "{}.{}({})",
265            palette.var("var"),
266            palette.description("contains"),
267            palette.expected(&self.pattern),
268        )
269    }
270}
271
272pub fn contains<P>(pattern: P) -> ContainsPredicate
284where
285    P: Into<String>,
286{
287    ContainsPredicate {
288        pattern: pattern.into(),
289    }
290}