1 | // Copyright (c) 2018 The predicates-rs Project Developers. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
4 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
5 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
6 | // option. This file may not be copied, modified, or distributed |
7 | // except according to those terms. |
8 | |
9 | use std::fmt; |
10 | |
11 | use crate::reflection; |
12 | use crate::utils; |
13 | use crate::Predicate; |
14 | |
15 | /// An error that occurred during parsing or compiling a regular expression. |
16 | pub type RegexError = regex::Error; |
17 | |
18 | /// Predicate that uses regex matching |
19 | /// |
20 | /// This is created by the `predicate::str::is_match`. |
21 | #[derive(Debug, Clone)] |
22 | pub struct RegexPredicate { |
23 | re: regex::Regex, |
24 | } |
25 | |
26 | impl RegexPredicate { |
27 | /// Require a specific count of matches. |
28 | /// |
29 | /// # Examples |
30 | /// |
31 | /// ``` |
32 | /// use predicates::prelude::*; |
33 | /// |
34 | /// let predicate_fn = predicate::str::is_match("T[a-z]*" ).unwrap().count(3); |
35 | /// assert_eq!(true, predicate_fn.eval("One Two Three Two One" )); |
36 | /// assert_eq!(false, predicate_fn.eval("One Two Three" )); |
37 | /// ``` |
38 | pub fn count(self, count: usize) -> RegexMatchesPredicate { |
39 | RegexMatchesPredicate { re: self.re, count } |
40 | } |
41 | } |
42 | |
43 | impl Predicate<str> for RegexPredicate { |
44 | fn eval(&self, variable: &str) -> bool { |
45 | self.re.is_match(variable) |
46 | } |
47 | |
48 | fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option<reflection::Case<'a>> { |
49 | utils::default_find_case(self, expected, variable) |
50 | .map(|case| case.add_product(reflection::Product::new("var" , variable.to_owned()))) |
51 | } |
52 | } |
53 | |
54 | impl reflection::PredicateReflection for RegexPredicate {} |
55 | |
56 | impl fmt::Display for RegexPredicate { |
57 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
58 | let palette = crate::Palette::current(); |
59 | write!( |
60 | f, |
61 | "{}.{}({})" , |
62 | palette.var.paint("var" ), |
63 | palette.description.paint("is_match" ), |
64 | palette.expected.paint(&self.re), |
65 | ) |
66 | } |
67 | } |
68 | |
69 | /// Predicate that checks for repeated patterns. |
70 | /// |
71 | /// This is created by `predicates::str::is_match(...).count`. |
72 | #[derive(Debug, Clone)] |
73 | pub struct RegexMatchesPredicate { |
74 | re: regex::Regex, |
75 | count: usize, |
76 | } |
77 | |
78 | impl Predicate<str> for RegexMatchesPredicate { |
79 | fn eval(&self, variable: &str) -> bool { |
80 | self.re.find_iter(variable).count() == self.count |
81 | } |
82 | |
83 | fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option<reflection::Case<'a>> { |
84 | let actual_count = self.re.find_iter(variable).count(); |
85 | let result = self.count == actual_count; |
86 | if result == expected { |
87 | Some( |
88 | reflection::Case::new(Some(self), result) |
89 | .add_product(reflection::Product::new("var" , variable.to_owned())) |
90 | .add_product(reflection::Product::new("actual count" , actual_count)), |
91 | ) |
92 | } else { |
93 | None |
94 | } |
95 | } |
96 | } |
97 | |
98 | impl reflection::PredicateReflection for RegexMatchesPredicate { |
99 | fn parameters<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Parameter<'a>> + 'a> { |
100 | let params = vec![reflection::Parameter::new("count" , &self.count)]; |
101 | Box::new(params.into_iter()) |
102 | } |
103 | } |
104 | |
105 | impl fmt::Display for RegexMatchesPredicate { |
106 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
107 | let palette = crate::Palette::current(); |
108 | write!( |
109 | f, |
110 | "{}.{}({})" , |
111 | palette.var.paint("var" ), |
112 | palette.description.paint("is_match" ), |
113 | palette.expected.paint(&self.re), |
114 | ) |
115 | } |
116 | } |
117 | |
118 | /// Creates a new `Predicate` that uses a regular expression to match the string. |
119 | /// |
120 | /// # Examples |
121 | /// |
122 | /// ``` |
123 | /// use predicates::prelude::*; |
124 | /// |
125 | /// let predicate_fn = predicate::str::is_match("^Hello.*$" ).unwrap(); |
126 | /// assert_eq!(true, predicate_fn.eval("Hello World" )); |
127 | /// assert_eq!(false, predicate_fn.eval("Food World" )); |
128 | /// ``` |
129 | pub fn is_match<S>(pattern: S) -> Result<RegexPredicate, RegexError> |
130 | where |
131 | S: AsRef<str>, |
132 | { |
133 | regex::Regex::new(pattern.as_ref()).map(|re| RegexPredicate { re }) |
134 | } |
135 | |