1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 use crate::description::Description;
16 use crate::matcher::{Matcher, MatcherBase, MatcherResult};
17 use regex::Regex;
18 use std::fmt::Debug;
19 use std::ops::Deref;
20
21 /// Matches a string containing a substring which matches the given regular
22 /// expression.
23 ///
24 /// Both the actual value and the expected regular expression may be either a
25 /// `String` or a string reference.
26 ///
27 /// ```
28 /// # use googletest::prelude::*;
29 /// # fn should_pass_1() -> Result<()> {
30 /// verify_that!("Some value", contains_regex("S.*e"))?; // Passes
31 /// # Ok(())
32 /// # }
33 /// # fn should_fail() -> Result<()> {
34 /// verify_that!("Another value", contains_regex("Some"))?; // Fails
35 /// # Ok(())
36 /// # }
37 /// # fn should_pass_2() -> Result<()> {
38 /// verify_that!("Some value".to_string(), contains_regex("v.*e"))?; // Passes
39 /// verify_that!("Some value", contains_regex("v.*e".to_string()))?; // Passes
40 /// # Ok(())
41 /// # }
42 /// # should_pass_1().unwrap();
43 /// # should_fail().unwrap_err();
44 /// # should_pass_2().unwrap();
45 /// ```
46 ///
47 /// Panics if the given `pattern` is not a syntactically valid regular
48 /// expression.
49 #[track_caller]
contains_regex<PatternT: Deref<Target = str>>(pattern: PatternT) -> ContainsRegexMatcher50 pub fn contains_regex<PatternT: Deref<Target = str>>(pattern: PatternT) -> ContainsRegexMatcher {
51 ContainsRegexMatcher { regex: Regex::new(pattern.deref()).unwrap() }
52 }
53
54 /// A matcher matching a string-like type containing a substring matching a
55 /// given regular expression.
56 ///
57 /// Intended only to be used from the function [`contains_regex`] only.
58 /// Should not be referenced by code outside this library.
59 #[derive(MatcherBase)]
60 pub struct ContainsRegexMatcher {
61 regex: Regex,
62 }
63
64 impl<ActualT: AsRef<str> + Debug + Copy> Matcher<ActualT> for ContainsRegexMatcher {
matches(&self, actual: ActualT) -> MatcherResult65 fn matches(&self, actual: ActualT) -> MatcherResult {
66 self.regex.is_match(actual.as_ref()).into()
67 }
68
describe(&self, matcher_result: MatcherResult) -> Description69 fn describe(&self, matcher_result: MatcherResult) -> Description {
70 match matcher_result {
71 MatcherResult::Match => {
72 format!("contains the regular expression {:#?}", self.regex.as_str()).into()
73 }
74 MatcherResult::NoMatch => {
75 format!("doesn't contain the regular expression {:#?}", self.regex.as_str()).into()
76 }
77 }
78 }
79 }
80
81 #[cfg(test)]
82 mod tests {
83 use crate::matcher::MatcherResult;
84 use crate::prelude::*;
85
86 #[test]
contains_regex_matches_string_reference_with_pattern() -> Result<()>87 fn contains_regex_matches_string_reference_with_pattern() -> Result<()> {
88 let matcher = contains_regex("S.*val");
89
90 let result = matcher.matches("Some value");
91
92 verify_that!(result, eq(MatcherResult::Match))
93 }
94
95 #[test]
contains_regex_does_not_match_string_without_pattern() -> Result<()>96 fn contains_regex_does_not_match_string_without_pattern() -> Result<()> {
97 let matcher = contains_regex("Another");
98
99 let result = matcher.matches("Some value");
100
101 verify_that!(result, eq(MatcherResult::NoMatch))
102 }
103
104 #[test]
contains_regex_matches_owned_string_with_pattern() -> Result<()>105 fn contains_regex_matches_owned_string_with_pattern() -> Result<()> {
106 let matcher = contains_regex("value");
107
108 let result = matcher.matches(&"Some value".to_string());
109
110 verify_that!(result, eq(MatcherResult::Match))
111 }
112
113 #[test]
contains_regex_matches_string_reference_with_owned_string() -> Result<()>114 fn contains_regex_matches_string_reference_with_owned_string() -> Result<()> {
115 let matcher = contains_regex("value");
116
117 let result = matcher.matches("Some value");
118
119 verify_that!(result, eq(MatcherResult::Match))
120 }
121
122 #[test]
verify_that_works_with_owned_string() -> Result<()>123 fn verify_that_works_with_owned_string() -> Result<()> {
124 verify_that!("Some value".to_string(), contains_regex("value"))
125 }
126
127 #[test]
contains_regex_displays_quoted_debug_of_pattern() -> Result<()>128 fn contains_regex_displays_quoted_debug_of_pattern() -> Result<()> {
129 let matcher = contains_regex("\n");
130
131 verify_that!(
132 Matcher::<&str>::describe(&matcher, MatcherResult::Match),
133 displays_as(eq("contains the regular expression \"\\n\""))
134 )
135 }
136 }
137