1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
pub struct Tokenizer<'a> {
    running_str: &'a str,
    idx: usize,
}

impl<'a> Tokenizer<'a> {
    pub fn new(inp: &'a str) -> Self {
        Self {
            running_str: inp,
            idx: 0,
        }
    }

    pub fn current_index(&self) -> usize {
        self.idx
    }

    fn next_token<P1, P2, P3>(
        &mut self,
        find_pattern: P1,
        starts_with_pattern: P2,
        strip_pattern: P3,
    ) -> Option<(usize, &'a str)>
    where
        P1: FnMut(char) -> bool,
        P2: FnMut(char) -> bool,
        P3: FnMut(char) -> bool,
    {
        if self.running_str.is_empty() {
            return None;
        }

        let i = self.running_str.find(find_pattern)?;

        let (value, rest) = self.running_str.split_at(i);

        if value.is_empty() {
            return None;
        }

        let rest_len = rest.len();

        self.running_str = rest
            .strip_prefix(starts_with_pattern)?
            .trim_start_matches(strip_pattern);

        let old_value_len = value.len();
        let value = value.trim_start();

        let leading_whitespace_size = value.len() - old_value_len;
        let pos_start = self.idx + leading_whitespace_size;
        self.idx += i + (rest_len - self.running_str.len());

        Some((pos_start, value.trim_end()))
    }

    pub fn next_ident(&mut self) -> Option<(usize, &'a str)> {
        self.next_token(
            |c| !c.is_alphanumeric() && c != ',' && c != '_',
            |c| c == '=',
            |c| c.is_whitespace(),
        )
    }

    pub fn next_value(&mut self) -> Option<(usize, &'a str)> {
        self.next_token(
            |c| c.is_whitespace() || c == ',' || c == '=',
            |c| c.is_whitespace() || c == ',',
            |c| c.is_whitespace() || c == ',',
        )
        .or_else(|| {
            let rest = self.running_str;
            self.running_str = "";
            let pos_start = self.idx;
            self.idx += rest.len();
            Some((pos_start, rest))
        })
    }
}