harper_core/patterns/
repeating_pattern.rs

1use super::Pattern;
2use crate::Token;
3
4/// A pattern that will match one or more repetitions of the same pattern.
5///
6/// Somewhat reminiscent of the `.*` operator in Regex.
7pub struct RepeatingPattern {
8    inner: Box<dyn Pattern>,
9    required_repetitions: usize,
10}
11
12impl RepeatingPattern {
13    pub fn new(pattern: Box<dyn Pattern>, required_repetitions: usize) -> Self {
14        Self {
15            inner: pattern,
16            required_repetitions,
17        }
18    }
19}
20
21impl Pattern for RepeatingPattern {
22    fn matches(&self, tokens: &[Token], source: &[char]) -> usize {
23        let mut tok_cursor = 0;
24        let mut repetition = 0;
25
26        loop {
27            let match_len = self.inner.matches(&tokens[tok_cursor..], source);
28
29            if match_len == 0 {
30                if repetition >= self.required_repetitions {
31                    return tok_cursor;
32                } else {
33                    return 0;
34                }
35            } else {
36                tok_cursor += match_len;
37                repetition += 1;
38            }
39        }
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::RepeatingPattern;
46    use crate::patterns::{AnyPattern, Pattern};
47    use crate::Document;
48
49    #[test]
50    fn matches_anything() {
51        let doc = Document::new_plain_english_curated(
52            "This matcher will match the entirety of any document!",
53        );
54        let pat = RepeatingPattern::new(Box::new(AnyPattern), 0);
55
56        assert_eq!(
57            pat.matches(doc.get_tokens(), doc.get_source()),
58            doc.get_tokens().len()
59        )
60    }
61
62    #[test]
63    fn does_not_match_short() {
64        let doc = Document::new_plain_english_curated("No match");
65        let pat = RepeatingPattern::new(Box::new(AnyPattern), 4);
66
67        assert_eq!(pat.matches(doc.get_tokens(), doc.get_source()), 0)
68    }
69}