1use std::fmt::Display;
9
10use anyhow::Result;
11use serde::Serialize;
12
13use crate::escaping::Escaper;
14use crate::newline::StringNewline;
15use crate::rules::registry::RuleRegistry;
16use crate::rules::rule::Rule;
17
18#[derive(Debug, Clone)]
21pub struct Expectation {
22 pub optional: bool,
24
25 pub multiline: bool,
27
28 pub rule: Box<dyn Rule>,
30
31 original: String,
33}
34
35impl Expectation {
36 pub fn unmake(&self) -> (String, Vec<u8>, bool, bool) {
38 let (kind, expression) = self.rule.unmake();
39 (kind, expression, self.optional, self.multiline)
40 }
41
42 pub fn matches(&self, line: &[u8]) -> bool {
44 self.rule.matches(line)
45 }
46
47 pub fn to_expression_string(&self, escaper: &Escaper) -> String {
49 self.rule
50 .to_expression_string(self.optional, self.multiline, escaper)
51 }
52
53 pub fn original_string(&self) -> String {
55 self.original.clone()
56 }
57}
58
59impl Display for Expectation {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 write!(f, "{}", self.to_expression_string(&Escaper::default()))
62 }
63}
64
65impl PartialEq for Expectation {
66 fn eq(&self, other: &Self) -> bool {
67 self.optional == other.optional
68 && self.multiline == other.multiline
69 && self.rule.to_string() == other.rule.to_string()
70 }
71}
72
73impl Serialize for Expectation {
74 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
75 where
76 S: serde::Serializer,
77 {
78 serializer.serialize_str(&self.original)
79 }
80}
81
82pub struct ExpectationMaker(RuleRegistry);
85
86impl ExpectationMaker {
87 pub fn new(registry: RuleRegistry) -> Self {
88 Self(registry)
89 }
90
91 pub fn parse(&self, line: &str) -> Result<Expectation> {
117 let (expression, kind, quantifier) = self.extract(line)?;
118 let multiline = quantifier == "*" || quantifier == "+";
119 let optional = quantifier == "*" || quantifier == "?";
120 self.make(
121 &kind,
122 &expression,
123 optional,
124 multiline,
125 &(&line).trim_newlines(),
126 )
127 }
128
129 pub(crate) fn make(
131 &self,
132 kind: &str,
133 expression: &str,
134 optional: bool,
135 multiline: bool,
136 original: &str,
137 ) -> Result<Expectation> {
138 Ok(Expectation {
139 optional,
140 multiline,
141 rule: self.0.make(kind, expression)?,
142 original: original.into(),
143 })
144 }
145
146 fn extract(&self, line: &str) -> Result<(String, String, String)> {
148 let captures = self
149 .0
150 .to_expectation_regex()?
151 .captures(line)
152 .map_or(vec![], |captures| {
153 captures
154 .iter()
155 .skip(1)
156 .filter_map(|m| m.map(|v| v.as_str()))
157 .collect::<Vec<_>>()
158 });
159 if captures.len() == 1 {
160 Ok((line.to_string(), "equal".to_string(), "".to_string()))
161 } else if captures.len() == 2 {
162 Ok((
163 captures[0].to_string(),
164 captures[1].to_string(),
165 "".to_string(),
166 ))
167 } else {
168 Ok((
169 captures[0].to_string(),
170 match captures[1] {
171 "" => "equal",
172 v => v,
173 }
174 .to_string(),
175 captures[2].to_string(),
176 ))
177 }
178 }
179}
180
181#[cfg(test)]
182pub(crate) mod tests {
183 use super::ExpectationMaker;
184 use crate::escaping::Escaper;
185 use crate::rules::registry::RuleRegistry;
186
187 #[test]
188 fn test_expectation_extract() {
189 let tests = vec![
190 ("foo", ("foo", "equal", "")),
191 ("foo (?)", ("foo", "equal", "?")),
192 ("foo (*)", ("foo", "equal", "*")),
193 ("foo (+)", ("foo", "equal", "+")),
194 ("foo (eq+)", ("foo", "eq", "+")),
195 ("foo (equal+)", ("foo", "equal", "+")),
196 ("foo (no-eol)", ("foo", "no-eol", "")),
197 ("foo (no-eol?)", ("foo", "no-eol", "?")),
198 ("foo (no-eol*)", ("foo", "no-eol", "*")),
199 ("foo (no-eol+)", ("foo", "no-eol", "+")),
200 ("foo (esc)", ("foo", "esc", "")),
201 ("foo (esc*)", ("foo", "esc", "*")),
202 ("foo (escaped)", ("foo", "escaped", "")),
203 ("foo (escaped+)", ("foo", "escaped", "+")),
204 ("foo (re)", ("foo", "re", "")),
205 ("foo (re?)", ("foo", "re", "?")),
206 ("foo (regex*)", ("foo", "regex", "*")),
207 ("foo (regex+)", ("foo", "regex", "+")),
208 ("foo (glob)", ("foo", "glob", "")),
209 ("foo (glob?)", ("foo", "glob", "?")),
210 ("foo (glob*)", ("foo", "glob", "*")),
211 ("foo (glob+)", ("foo", "glob", "+")),
212 ("foo (glob+) (glob+)", ("foo (glob+)", "glob", "+")),
213 ];
214
215 tests.iter().for_each(
216 |(line, (expect_expression, expect_kind, expect_quantifier))| {
217 let (expression, kind, quantifier) = expectation_maker()
218 .extract(line)
219 .expect("extract expression from line");
220 assert_eq!(
221 expect_expression.to_string(),
222 expression,
223 "expression from '{line}'"
224 );
225 assert_eq!(expect_kind.to_string(), kind, "kind from '{line}'");
226 assert_eq!(
227 expect_quantifier.to_string(),
228 quantifier,
229 "quantifier from '{line}'"
230 );
231 },
232 );
233 }
234
235 #[test]
236 fn test_parse_to_expression_string() {
237 let tests = vec![
238 ("foo", "foo"),
239 ("foo (?)", "foo (?)"),
240 ("foo (equal)", "foo"),
241 ("foo (eq)", "foo"),
242 ("foo (equal*)", "foo (*)"),
243 ("foo (no-eol)", "foo (no-eol)"),
244 ("foo (escaped)", "foo (escaped)"),
245 ("foo (esc)", "foo (escaped)"),
246 ("foo (esc+)", "foo (escaped+)"),
247 ("foo (glob)", "foo (glob)"),
248 ("foo (gl)", "foo (glob)"),
249 ("foo (glob?)", "foo (glob?)"),
250 ("foo (regex)", "foo (regex)"),
251 ("foo (re)", "foo (regex)"),
252 ("foo (regex*)", "foo (regex*)"),
253 ];
254 for (from, to) in tests {
255 let expectation = expectation_maker()
256 .parse(from)
257 .unwrap_or_else(|_| panic!("parse `{from}`"));
258 let rendered = expectation.to_expression_string(&Escaper::default());
259 assert_eq!(rendered, *to, "`{from}` rendered back to `{to}`");
260 }
261 }
262
263 pub(crate) fn expectation_maker() -> ExpectationMaker {
264 ExpectationMaker::new(RuleRegistry::default())
265 }
266
267 #[macro_export]
268 macro_rules! test_expectation {
269 ($expression:expr) => {
270 $crate::expectation::tests::expectation_maker()
271 .make("equal", $expression, false, false, $expression)
272 .expect("create test expectation")
273 };
274 ($kind:expr, $expression:expr) => {
275 $crate::expectation::tests::expectation_maker()
276 .make(
277 $kind,
278 $expression,
279 false,
280 false,
281 &format!("{} ({})", $expression, $kind),
282 )
283 .expect("create test expectation")
284 };
285 ($kind:expr, $expression:expr, $optional:expr, $multiline:expr) => {
286 $crate::expectation::tests::expectation_maker()
287 .make(
288 $kind,
289 $expression,
290 $optional,
291 $multiline,
292 &format!("{} ({})", $expression, $kind),
293 )
294 .expect("create test expectation")
295 };
296 ($kind:expr, $expression:expr, $optional:expr, $multiline:expr, $original:expr) => {
297 $crate::expectation::tests::expectation_maker()
298 .make($kind, $expression, $optional, $multiline, $original)
299 .expect("create test expectation")
300 };
301 }
302}