litcheck_filecheck/pattern/matcher/matchers/
whitespace.rs

1use crate::common::*;
2
3/// This matcher accepts only ASCII whitespace characters,
4/// and is always anchored, i.e. it will never match input
5/// that starts with a non-ASCII whitespace character.
6#[derive(Debug)]
7pub struct AsciiWhitespaceMatcher {
8    span: SourceSpan,
9}
10impl AsciiWhitespaceMatcher {
11    pub const fn new(span: SourceSpan) -> Self {
12        Self { span }
13    }
14}
15impl MatcherMut for AsciiWhitespaceMatcher {
16    #[inline]
17    fn try_match_mut<'input, 'context, C>(
18        &self,
19        input: Input<'input>,
20        context: &mut C,
21    ) -> DiagResult<MatchResult<'input>>
22    where
23        C: Context<'input, 'context> + ?Sized,
24    {
25        self.try_match(input, context)
26    }
27}
28impl Matcher for AsciiWhitespaceMatcher {
29    #[inline]
30    fn try_match<'input, 'context, C>(
31        &self,
32        input: Input<'input>,
33        context: &C,
34    ) -> DiagResult<MatchResult<'input>>
35    where
36        C: Context<'input, 'context> + ?Sized,
37    {
38        let buffer = input.as_slice();
39        if input.is_empty() || !buffer[0].is_ascii_whitespace() {
40            return Ok(MatchResult::failed(
41                CheckFailedError::MatchNoneButExpected {
42                    span: self.span,
43                    match_file: context.match_file(),
44                    note: None,
45                },
46            ));
47        }
48
49        let start = input.start();
50        let mut len = 0;
51        for c in buffer.iter() {
52            match c {
53                b'\n' => break,
54                c if !c.is_ascii_whitespace() => break,
55                _ => {
56                    len += 1;
57                }
58            }
59        }
60
61        Ok(MatchResult::ok(MatchInfo::new(
62            start..(start + len),
63            self.span,
64        )))
65    }
66}
67impl Spanned for AsciiWhitespaceMatcher {
68    #[inline(always)]
69    fn span(&self) -> SourceSpan {
70        self.span
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_whitespace_matcher() -> DiagResult<()> {
80        let mut input = String::new();
81        input.push('\t');
82        input.push('\r');
83        input.push(' ');
84        input.push('\n');
85        input.push_str("abc");
86        input.push('\n');
87
88        let mut context = TestContext::new();
89        context.with_checks("").with_input(input.clone());
90
91        let mctx = context.match_context();
92
93        let matcher = AsciiWhitespaceMatcher::new(SourceSpan::from(0..0));
94        let bytes = input.as_bytes();
95        let input = Input::new(bytes, false).span(0..);
96        let result = matcher.try_match(input, &mctx)?;
97        let info = result.info.expect("expected match");
98        assert_eq!(info.span.offset(), 0);
99        assert_eq!(info.span.len(), 3);
100        Ok(())
101    }
102}