path_tree/
parser.rs

1use alloc::{string::ToString, vec::Vec};
2use core::{iter::Peekable, str::CharIndices};
3
4#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
5pub enum Kind {
6    /// `:` 58
7    /// `:name`
8    Normal,
9    /// `?` 63
10    /// Optional: `:name?-`
11    Optional,
12    /// Optional segment: `/:name?/` or `/:name?`
13    OptionalSegment,
14    // Optional,
15    /// `+` 43
16    OneOrMore,
17    /// `*` 42
18    /// Zero or more: `*-`
19    ZeroOrMore,
20    /// Zero or more segment: `/*/` or `/*`
21    ZeroOrMoreSegment,
22    // TODO: regexp
23}
24
25#[derive(Clone, Debug, PartialEq, Eq)]
26pub enum Piece {
27    String(Vec<u8>),
28    Parameter(Position, Kind),
29}
30
31#[derive(Clone, Debug, PartialEq, Eq)]
32pub enum Position {
33    Index(usize, Vec<u8>),
34    Named(Vec<u8>),
35}
36
37pub struct Parser<'a> {
38    pos: usize,
39    count: usize,
40    input: &'a str,
41    cursor: Peekable<CharIndices<'a>>,
42}
43
44impl<'a> Parser<'a> {
45    #[must_use]
46    pub fn new(input: &'a str) -> Self {
47        Self {
48            input,
49            pos: 0,
50            count: 0,
51            cursor: input.char_indices().peekable(),
52        }
53    }
54
55    fn string(&mut self) -> &'a [u8] {
56        let mut start = self.pos;
57        while let Some(&(i, c)) = self.cursor.peek() {
58            match c {
59                '\\' => {
60                    if start < i {
61                        self.pos = i;
62                        return self.input[start..i].as_bytes();
63                    }
64
65                    self.cursor.next();
66                    if let Some(&(j, c)) = self.cursor.peek() {
67                        // removes `\`
68                        if c == '\\' {
69                            start = j;
70                        } else {
71                            self.cursor.next();
72                            self.pos = j + c.len_utf8();
73                            return self.input[j..self.pos].as_bytes();
74                        }
75                    }
76                }
77                ':' | '+' | '*' => {
78                    self.pos = i + 1;
79                    return self.input[start..i].as_bytes();
80                }
81                _ => {
82                    self.cursor.next();
83                }
84            }
85        }
86
87        self.input[start..].as_bytes()
88    }
89
90    fn parameter(&mut self) -> (Position, Kind) {
91        let start = self.pos;
92        while let Some(&(i, c)) = self.cursor.peek() {
93            match c {
94                '-' | '.' | '~' | '/' | '\\' | ':' => {
95                    self.pos = i;
96                    return (
97                        Position::Named(self.input[start..i].as_bytes().to_vec()),
98                        Kind::Normal,
99                    );
100                }
101                '?' | '+' | '*' => {
102                    self.cursor.next();
103                    self.pos = i + 1;
104                    return (
105                        Position::Named(self.input[start..i].as_bytes().to_vec()),
106                        if c == '+' {
107                            Kind::OneOrMore
108                        } else {
109                            let f = {
110                                let prefix = start >= 2
111                                    && (self.input.get(start - 2..start - 1) == Some("/"));
112                                let suffix = self.cursor.peek().is_none_or(|(_, c)| *c == '/');
113                                prefix && suffix
114                            };
115                            if c == '?' {
116                                if f {
117                                    Kind::OptionalSegment
118                                } else {
119                                    Kind::Optional
120                                }
121                            } else if f {
122                                Kind::ZeroOrMoreSegment
123                            } else {
124                                Kind::ZeroOrMore
125                            }
126                        },
127                    );
128                }
129                _ => {
130                    self.cursor.next();
131                }
132            }
133        }
134
135        (
136            Position::Named(self.input[start..].as_bytes().to_vec()),
137            Kind::Normal,
138        )
139    }
140}
141
142impl Iterator for Parser<'_> {
143    type Item = Piece;
144
145    fn next(&mut self) -> Option<Self::Item> {
146        match self.cursor.peek() {
147            Some(&(i, c)) => match c {
148                ':' => {
149                    self.cursor.next();
150                    self.pos = i + 1;
151                    let (position, kind) = self.parameter();
152                    Some(Piece::Parameter(position, kind))
153                }
154                '+' | '*' => {
155                    self.cursor.next();
156                    self.count += 1;
157                    self.pos = i + 1;
158                    Some(Piece::Parameter(
159                        Position::Index(self.count, {
160                            let mut s = Vec::new();
161                            s.push(c as u8);
162                            s.extend_from_slice(self.count.to_string().as_bytes());
163                            s
164                        }),
165                        if c == '+' {
166                            Kind::OneOrMore
167                        } else {
168                            let f = {
169                                let prefix = i >= 1 && (self.input.get(i - 1..i) == Some("/"));
170                                let suffix = self.cursor.peek().is_none_or(|(_, c)| *c == '/');
171                                prefix && suffix
172                            };
173                            if f {
174                                Kind::ZeroOrMoreSegment
175                            } else {
176                                Kind::ZeroOrMore
177                            }
178                        },
179                    ))
180                }
181                _ => Some(Piece::String(self.string().to_vec())),
182            },
183            None => None,
184        }
185    }
186}