Skip to main content

ferroui_base/utilities/
keyword_parser.rs

1//! Keyword look-ahead on top of [`CharacterReader`].
2
3use super::character_reader::CharacterReader;
4
5impl CharacterReader<'_> {
6    /// Checks whether the input continues with optional white space followed
7    /// by `keyword`, without consuming anything.
8    pub fn check_keyword(&self, keyword: &str) -> bool {
9        self.check_keyword_internal(keyword).is_some()
10    }
11
12    /// Returns the number of characters (white space plus keyword) the keyword
13    /// occupies, or `None` when the input does not continue with it.
14    fn check_keyword_internal(&self, keyword: &str) -> Option<usize> {
15        let ws = self.peek_whitespace();
16        let count = ws.chars().count() + keyword.chars().count();
17
18        let chars = self.try_peek(count);
19        if chars.is_empty() {
20            return None;
21        }
22        if &chars[ws.len()..] == keyword {
23            return Some(count);
24        }
25        None
26    }
27
28    /// Consumes optional white space followed by `keyword` if the input
29    /// continues with it.
30    ///
31    /// As upstream, the consumed characters are skipped without advancing
32    /// [`CharacterReader::position`].
33    pub fn take_if_keyword(&mut self, keyword: &str) -> bool {
34        match self.check_keyword_internal(keyword) {
35            Some(l) => {
36                self.skip(l);
37                true
38            }
39            None => false,
40        }
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn check_keyword_ignores_leading_whitespace() {
50        let r = CharacterReader::new("  :> Foo");
51        assert!(r.check_keyword(":>"));
52        assert!(!r.check_keyword(":="));
53        assert_eq!(r.peek(), Some(' '));
54    }
55
56    #[test]
57    fn check_keyword_fails_on_short_input() {
58        assert!(!CharacterReader::new(" a").check_keyword("as "));
59        assert!(!CharacterReader::new("").check_keyword(":="));
60    }
61
62    #[test]
63    fn take_if_keyword_consumes_whitespace_and_keyword() {
64        let mut r = CharacterReader::new(" as Foo");
65        assert!(!r.take_if_keyword(":="));
66        assert!(r.take_if_keyword("as "));
67        assert_eq!(r.peek(), Some('F'));
68        // Upstream quirk: the position is not advanced.
69        assert_eq!(r.position(), 0);
70        assert!(!r.take_if_keyword("as "));
71    }
72}