1use crate::{
2 error::{ExpectedToken, ParserErrorReason},
3 parser::{CaptureOrExact, RefCaptureVariant, RouteParserToken},
4 ParseError,
5};
6use nom::{
7 branch::alt,
8 bytes::complete::{tag, take_till1},
9 character::{
10 complete::{char, digit1},
11 is_digit,
12 },
13 combinator::{map, map_parser},
14 error::ErrorKind,
15 sequence::{delimited, separated_pair},
16 IResult,
17};
18
19#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Ord, PartialOrd)]
21pub enum FieldNamingScheme {
22 Named,
24 Unnamed,
26 Unit,
28}
29
30pub fn get_slash(i: &str) -> IResult<&str, RouteParserToken, ParseError> {
31 map(char('/'), |_: char| RouteParserToken::Separator)(i)
32 .map_err(|_: nom::Err<()>| nom::Err::Error(ParseError::expected(ExpectedToken::Separator)))
33}
34
35pub fn get_question(i: &str) -> IResult<&str, RouteParserToken, ParseError> {
36 map(char('?'), |_: char| RouteParserToken::QueryBegin)(i)
37 .map_err(|_: nom::Err<()>| nom::Err::Error(ParseError::expected(ExpectedToken::QueryBegin)))
38}
39
40pub fn get_and(i: &str) -> IResult<&str, RouteParserToken, ParseError> {
41 map(char('&'), |_: char| RouteParserToken::QuerySeparator)(i).map_err(|_: nom::Err<()>| {
42 nom::Err::Error(ParseError::expected(ExpectedToken::QuerySeparator))
43 })
44}
45
46pub fn get_hash(i: &str) -> IResult<&str, RouteParserToken, ParseError> {
48 map(char('#'), |_: char| RouteParserToken::FragmentBegin)(i).map_err(|_: nom::Err<()>| {
49 nom::Err::Error(ParseError::expected(ExpectedToken::FragmentBegin))
50 })
51}
52
53pub fn get_end(i: &str) -> IResult<&str, RouteParserToken, ParseError> {
55 map(char('!'), |_: char| RouteParserToken::End)(i)
56 .map_err(|_: nom::Err<()>| nom::Err::Error(ParseError::expected(ExpectedToken::End)))
57}
58
59fn get_open_bracket(i: &str) -> IResult<&str, (), ParseError> {
61 map(char('{'), |_: char| ())(i).map_err(|_: nom::Err<()>| {
62 nom::Err::Error(ParseError::expected(ExpectedToken::OpenBracket))
63 })
64}
65
66fn get_close_bracket(i: &str) -> IResult<&str, (), ParseError> {
67 map(char('}'), |_: char| ())(i).map_err(|_: nom::Err<()>| {
68 nom::Err::Error(ParseError::expected(ExpectedToken::CloseBracket))
69 })
70}
71
72fn get_eq(i: &str) -> IResult<&str, (), ParseError> {
73 map(char('='), |_: char| ())(i)
74 .map_err(|_: nom::Err<()>| nom::Err::Error(ParseError::expected(ExpectedToken::Equals)))
75}
76
77fn get_star(i: &str) -> IResult<&str, (), ParseError> {
78 map(char('*'), |_: char| ())(i)
79 .map_err(|_: nom::Err<()>| nom::Err::Error(ParseError::expected(ExpectedToken::Star)))
80}
81
82fn get_colon(i: &str) -> IResult<&str, (), ParseError> {
83 map(char(':'), |_: char| ())(i)
84 .map_err(|_: nom::Err<()>| nom::Err::Error(ParseError::expected(ExpectedToken::Colon)))
85}
86
87fn rust_ident(i: &str) -> IResult<&str, &str, ParseError> {
88 let invalid_ident_chars = r##" \|/{[]()?+=-!@#$%^&*~`'";:"##;
89 map_parser(take_till1(move |c| c == '}'), move |i: &str| {
92 match take_till1::<_, _, ()>(|c| invalid_ident_chars.contains(c))(i) {
93 Ok((remain, got)) => {
94 if !got.is_empty() && got.starts_with(|c: char| is_digit(c as u8)) {
96 Err(nom::Err::Failure(ParseError {
97 reason: Some(ParserErrorReason::BadRustIdent(got.chars().next().unwrap())),
98 expected: vec![ExpectedToken::Ident],
99 offset: 1,
100 }))
101 } else if !remain.is_empty() {
102 Err(nom::Err::Failure(ParseError {
103 reason: Some(ParserErrorReason::BadRustIdent(
104 remain.chars().next().unwrap(),
105 )),
106 expected: vec![ExpectedToken::CloseBracket, ExpectedToken::Ident],
107 offset: got.len() + 1,
108 }))
109 } else {
110 Ok((i, i))
111 }
112 }
113 Err(_) => Ok((i, i)),
114 }
115 })(i)
116}
117
118fn escaped_item_impl(i: &str) -> IResult<&str, &str> {
120 map(alt((tag("!!"), tag("{{"), tag("}}"))), |s| match s {
121 "!!" => "!",
122 "}}" => "}",
123 "{{" => "{",
124 _ => unreachable!(),
125 })(i)
126}
127
128pub fn nothing(i: &str) -> IResult<&str, RouteParserToken, ParseError> {
130 if i.is_empty() {
131 Ok((i, RouteParserToken::Nothing))
132 } else {
133 Err(nom::Err::Error(ParseError {
134 reason: None, expected: vec![],
136 offset: 0,
137 }))
138 }
139}
140
141fn exact_impl(special_chars: &'static str) -> impl Fn(&str) -> IResult<&str, &str, ParseError> {
146 move |i: &str| {
150 alt((
151 take_till1(move |c| special_chars.contains(c)),
152 escaped_item_impl,
153 ))(i)
154 .map_err(|x: nom::Err<(&str, ErrorKind)>| {
155 let s = match x {
156 nom::Err::Error((s, _)) | nom::Err::Failure((s, _)) => s,
157 nom::Err::Incomplete(_) => panic!(),
158 };
159 nom::Err::Error(ParseError {
160 reason: Some(ParserErrorReason::BadLiteral),
161 expected: vec![ExpectedToken::Literal],
162 offset: 1 + i.len() - s.len(),
163 })
164 })
165 }
166}
167
168const SPECIAL_CHARS: &str = r##"/?&#={}!"##;
169const FRAGMENT_SPECIAL_CHARS: &str = r##"{}!"##;
170
171pub fn exact(i: &str) -> IResult<&str, RouteParserToken, ParseError> {
172 map(exact_impl(SPECIAL_CHARS), RouteParserToken::Exact)(i)
173}
174
175pub fn fragment_exact(i: &str) -> IResult<&str, RouteParserToken, ParseError> {
177 map(exact_impl(FRAGMENT_SPECIAL_CHARS), RouteParserToken::Exact)(i)
178}
179
180pub fn capture<'a>(
181 field_naming_scheme: FieldNamingScheme,
182) -> impl Fn(&'a str) -> IResult<&'a str, RouteParserToken<'a>, ParseError> {
183 map(capture_impl(field_naming_scheme), RouteParserToken::Capture)
184}
185
186fn capture_single_impl<'a>(
187 field_naming_scheme: FieldNamingScheme,
188) -> impl Fn(&'a str) -> IResult<&'a str, RefCaptureVariant<'a>, ParseError> {
189 move |i: &str| match field_naming_scheme {
190 FieldNamingScheme::Named => delimited(
191 get_open_bracket,
192 named::single_capture_impl,
193 get_close_bracket,
194 )(i),
195 FieldNamingScheme::Unnamed => delimited(
196 get_open_bracket,
197 alt((named::single_capture_impl, unnamed::single_capture_impl)),
198 get_close_bracket,
199 )(i),
200 FieldNamingScheme::Unit => {
201 println!("Unit encountered, erroring in capture single");
202 Err(nom::Err::Failure(ParseError {
203 reason: Some(ParserErrorReason::CapturesInUnit),
204 expected: vec![],
205 offset: 0,
206 }))
207 }
208 }
209}
210
211fn capture_impl<'a>(
215 field_naming_scheme: FieldNamingScheme,
216) -> impl Fn(&'a str) -> IResult<&'a str, RefCaptureVariant, ParseError> {
217 move |i: &str| match field_naming_scheme {
218 FieldNamingScheme::Named => {
219 let inner = alt((
220 named::many_capture_impl,
221 named::numbered_capture_impl,
222 named::single_capture_impl,
223 ));
224 delimited(get_open_bracket, inner, get_close_bracket)(i)
225 }
226 FieldNamingScheme::Unnamed => {
227 let inner = alt((
228 named::many_capture_impl,
229 unnamed::many_capture_impl,
230 named::numbered_capture_impl,
231 unnamed::numbered_capture_impl,
232 named::single_capture_impl,
233 unnamed::single_capture_impl,
234 ));
235 delimited(get_open_bracket, inner, get_close_bracket)(i)
236 }
237 FieldNamingScheme::Unit => Err(nom::Err::Error(ParseError {
238 reason: Some(ParserErrorReason::CapturesInUnit),
239 expected: vec![],
240 offset: 0,
241 })),
242 }
243}
244
245mod named {
246 use super::*;
247 pub fn single_capture_impl(i: &str) -> IResult<&str, RefCaptureVariant, ParseError> {
248 map(rust_ident, |key| RefCaptureVariant::Named(key))(i)
249 }
250
251 pub fn many_capture_impl(i: &str) -> IResult<&str, RefCaptureVariant, ParseError> {
252 map(
253 separated_pair(get_star, get_colon, rust_ident),
254 |(_, key)| RefCaptureVariant::ManyNamed(key),
255 )(i)
256 }
257
258 pub fn numbered_capture_impl(i: &str) -> IResult<&str, RefCaptureVariant, ParseError> {
259 map(
260 separated_pair(digit1, get_colon, rust_ident),
261 |(number, key)| RefCaptureVariant::NumberedNamed {
262 sections: number.parse().unwrap(),
263 name: key,
264 },
265 )(i)
266 }
267}
268
269mod unnamed {
270 use super::*;
271
272 pub fn single_capture_impl(i: &str) -> IResult<&str, RefCaptureVariant, ParseError> {
275 Ok((i, RefCaptureVariant::Unnamed))
276 }
277
278 pub fn many_capture_impl(i: &str) -> IResult<&str, RefCaptureVariant, ParseError> {
279 map(get_star, |_| RefCaptureVariant::ManyUnnamed)(i)
280 }
281
282 pub fn numbered_capture_impl(i: &str) -> IResult<&str, RefCaptureVariant, ParseError> {
283 map(digit1, |number: &str| RefCaptureVariant::NumberedUnnamed {
284 sections: number.parse().unwrap(),
285 })(i)
286 }
287}
288
289fn cap_or_exact<'a>(
291 field_naming_scheme: FieldNamingScheme,
292) -> impl Fn(&'a str) -> IResult<&'a str, CaptureOrExact<'a>, ParseError> {
293 move |i: &str| {
294 alt((
295 map(
296 capture_single_impl(field_naming_scheme),
297 CaptureOrExact::Capture,
298 ),
299 map(exact_impl(SPECIAL_CHARS), CaptureOrExact::Exact),
300 ))(i)
301 }
302}
303
304pub fn query<'a>(
306 field_naming_scheme: FieldNamingScheme,
307) -> impl Fn(&'a str) -> IResult<&'a str, RouteParserToken<'a>, ParseError> {
308 move |i: &str| {
309 map(
310 separated_pair(
311 exact_impl(SPECIAL_CHARS),
312 get_eq,
313 cap_or_exact(field_naming_scheme),
314 ),
315 |(ident, capture_or_exact)| RouteParserToken::Query {
316 ident,
317 capture_or_exact,
318 },
319 )(i)
320 }
321}
322
323#[cfg(test)]
324mod test {
325 use super::*;
326
327 #[test]
328 fn lit() {
329 let x = exact("hello").expect("Should parse");
330 assert_eq!(x.1, RouteParserToken::Exact("hello"))
331 }
332
333 #[test]
334 fn cap_or_exact_match_lit() {
335 cap_or_exact(FieldNamingScheme::Named)("lorem").expect("Should parse");
336 }
337 #[test]
338 fn cap_or_exact_match_cap() {
339 cap_or_exact(FieldNamingScheme::Named)("{lorem}").expect("Should parse");
340 }
341
342 #[test]
343 fn query_section_exact() {
344 query(FieldNamingScheme::Named)("lorem=ipsum").expect("should parse");
345 }
346
347 #[test]
348 fn query_section_capture_named() {
349 query(FieldNamingScheme::Named)("lorem={ipsum}").expect("should parse");
350 }
351 #[test]
352 fn query_section_capture_named_fails_without_key() {
353 query(FieldNamingScheme::Named)("lorem={}").expect_err("should not parse");
354 }
355 #[test]
356 fn query_section_capture_unnamed_succeeds_without_key() {
357 query(FieldNamingScheme::Unnamed)("lorem={}").expect("should parse");
358 }
359
360 #[test]
361 fn non_leading_numbers_in_ident() {
362 rust_ident("hello5").expect("sholud parse");
363 }
364 #[test]
365 fn leading_numbers_in_ident_fails() {
366 rust_ident("5hello").expect_err("sholud not parse");
367 }
368}