yew_router_nested_route_parser/
parser.rs

1//! Parser that consumes a string and produces the first representation of the matcher.
2use crate::{
3    core::{
4        capture, exact, fragment_exact, get_and, get_end, get_hash, get_question, get_slash,
5        nothing, query,
6    },
7    error::{get_reason, ParseError, ParserErrorReason, PrettyParseError},
8    FieldNamingScheme,
9};
10use nom::{branch::alt, IResult};
11// use crate::core::escaped_item;
12
13/// Tokens generated from parsing a route matcher string.
14/// They will be optimized to another token type that is used to match URLs.
15#[derive(Debug, Clone, Copy, PartialEq)]
16pub enum RouteParserToken<'a> {
17    /// Generated by the empty string `""`.
18    Nothing,
19    /// Match /
20    Separator,
21    /// Match a specific string.
22    Exact(&'a str),
23    /// Match {_}. See `RefCaptureVariant` for more.
24    Capture(RefCaptureVariant<'a>),
25    /// Match ?
26    QueryBegin,
27    /// Match &
28    QuerySeparator,
29    /// Match x=y
30    Query {
31        /// Identifier
32        ident: &'a str,
33        /// Capture or match
34        capture_or_exact: CaptureOrExact<'a>,
35    },
36    /// Match \#
37    FragmentBegin,
38    /// Match !
39    End,
40}
41
42/// Token representing various types of captures.
43///
44/// It can capture and discard for unnamed variants, or capture and store in the `Matches` for the
45/// named variants.
46///
47/// Its name stems from the fact that it does not have ownership over all its values.
48/// It gets converted to CaptureVariant, a nearly identical enum that has owned Strings instead.
49#[derive(Debug, Clone, Copy, PartialEq)]
50pub enum RefCaptureVariant<'a> {
51    /// {}
52    Unnamed,
53    /// {*}
54    ManyUnnamed,
55    /// {5}
56    NumberedUnnamed {
57        /// Number of sections to match.
58        sections: usize,
59    },
60    /// {name} - captures a section and adds it to the map with a given name.
61    Named(&'a str),
62    /// {*:name} - captures over many sections and adds it to the map with a given name.
63    ManyNamed(&'a str),
64    /// {2:name} - captures a fixed number of sections with a given name.
65    NumberedNamed {
66        /// Number of sections to match.
67        sections: usize,
68        /// The key to be entered in the `Matches` map.
69        name: &'a str,
70    },
71}
72
73/// Either a Capture, or an Exact match
74#[derive(Debug, Clone, Copy, PartialEq)]
75pub enum CaptureOrExact<'a> {
76    /// Match a specific string.
77    Exact(&'a str),
78    /// Match a capture variant.
79    Capture(RefCaptureVariant<'a>),
80}
81
82/// Represents the states the parser can be in.
83#[derive(Clone, PartialEq)]
84enum ParserState<'a> {
85    None,
86    Path { prev_token: RouteParserToken<'a> },
87    FirstQuery { prev_token: RouteParserToken<'a> },
88    NthQuery { prev_token: RouteParserToken<'a> },
89    Fragment { prev_token: RouteParserToken<'a> },
90    End,
91}
92impl<'a> ParserState<'a> {
93    /// Given a new route parser token, transition to a new state.
94    ///
95    /// This will set the prev token to a token able to be handled by the new state,
96    /// so the new state does not need to handle arbitrary "from" states.
97    ///
98    /// This function represents the valid state transition graph.
99    fn transition(self, token: RouteParserToken<'a>) -> Result<Self, ParserErrorReason> {
100        match self {
101            ParserState::None => match token {
102                RouteParserToken::Separator
103                | RouteParserToken::Exact(_)
104                | RouteParserToken::Capture(_) => Ok(ParserState::Path { prev_token: token }),
105                RouteParserToken::QueryBegin => Ok(ParserState::FirstQuery { prev_token: token }),
106                RouteParserToken::QuerySeparator => Ok(ParserState::NthQuery { prev_token: token }),
107                RouteParserToken::Query { .. } => Err(ParserErrorReason::NotAllowedStateTransition),
108                RouteParserToken::FragmentBegin => Ok(ParserState::Fragment { prev_token: token }),
109                RouteParserToken::Nothing | RouteParserToken::End => Ok(ParserState::End),
110            },
111            ParserState::Path { prev_token } => {
112                match prev_token {
113                    RouteParserToken::Separator => match token {
114                        RouteParserToken::Exact(_) | RouteParserToken::Capture(_) => {
115                            Ok(ParserState::Path { prev_token: token })
116                        }
117                        RouteParserToken::QueryBegin => {
118                            Ok(ParserState::FirstQuery { prev_token: token })
119                        }
120                        RouteParserToken::FragmentBegin => {
121                            Ok(ParserState::Fragment { prev_token: token })
122                        }
123                        RouteParserToken::End => Ok(ParserState::End),
124                        _ => Err(ParserErrorReason::NotAllowedStateTransition),
125                    },
126                    RouteParserToken::Exact(_) => match token {
127                        RouteParserToken::Exact(_)
128                        | RouteParserToken::Separator
129                        | RouteParserToken::Capture(_) => {
130                            Ok(ParserState::Path { prev_token: token })
131                        }
132                        RouteParserToken::QueryBegin => {
133                            Ok(ParserState::FirstQuery { prev_token: token })
134                        }
135                        RouteParserToken::FragmentBegin => {
136                            Ok(ParserState::Fragment { prev_token: token })
137                        }
138                        RouteParserToken::End => Ok(ParserState::End),
139                        _ => Err(ParserErrorReason::NotAllowedStateTransition),
140                    },
141                    RouteParserToken::Capture(_) => match token {
142                        RouteParserToken::Separator | RouteParserToken::Exact(_) => {
143                            Ok(ParserState::Path { prev_token: token })
144                        }
145                        RouteParserToken::QueryBegin => {
146                            Ok(ParserState::FirstQuery { prev_token: token })
147                        }
148                        RouteParserToken::FragmentBegin => {
149                            Ok(ParserState::Fragment { prev_token: token })
150                        }
151                        RouteParserToken::End => Ok(ParserState::End),
152                        _ => Err(ParserErrorReason::NotAllowedStateTransition),
153                    },
154                    _ => Err(ParserErrorReason::InvalidState), /* Other previous token types are
155                                                                * invalid within a Path state. */
156                }
157            }
158            ParserState::FirstQuery { prev_token } => match prev_token {
159                RouteParserToken::QueryBegin => match token {
160                    RouteParserToken::Query { .. } => {
161                        Ok(ParserState::FirstQuery { prev_token: token })
162                    }
163                    _ => Err(ParserErrorReason::NotAllowedStateTransition),
164                },
165                RouteParserToken::Query { .. } => match token {
166                    RouteParserToken::QuerySeparator => {
167                        Ok(ParserState::NthQuery { prev_token: token })
168                    }
169                    RouteParserToken::FragmentBegin => {
170                        Ok(ParserState::Fragment { prev_token: token })
171                    }
172                    RouteParserToken::End => Ok(ParserState::End),
173                    _ => Err(ParserErrorReason::NotAllowedStateTransition),
174                },
175                _ => Err(ParserErrorReason::InvalidState),
176            },
177            ParserState::NthQuery { prev_token } => match prev_token {
178                RouteParserToken::QuerySeparator => match token {
179                    RouteParserToken::Query { .. } => {
180                        Ok(ParserState::NthQuery { prev_token: token })
181                    }
182                    _ => Err(ParserErrorReason::NotAllowedStateTransition),
183                },
184                RouteParserToken::Query { .. } => match token {
185                    RouteParserToken::QuerySeparator => {
186                        Ok(ParserState::NthQuery { prev_token: token })
187                    }
188                    RouteParserToken::FragmentBegin => {
189                        Ok(ParserState::Fragment { prev_token: token })
190                    }
191                    RouteParserToken::End => Ok(ParserState::End),
192                    _ => Err(ParserErrorReason::NotAllowedStateTransition),
193                },
194                _ => Err(ParserErrorReason::InvalidState),
195            },
196            ParserState::Fragment { prev_token } => match prev_token {
197                RouteParserToken::FragmentBegin
198                | RouteParserToken::Exact(_)
199                | RouteParserToken::Capture(_) => Ok(ParserState::Fragment { prev_token: token }),
200                RouteParserToken::End => Ok(ParserState::End),
201                _ => Err(ParserErrorReason::InvalidState),
202            },
203            ParserState::End => Err(ParserErrorReason::TokensAfterEndToken),
204        }
205    }
206}
207
208/// Parse a matching string into a vector of RouteParserTokens.
209///
210/// The parsing logic involves using a state machine.
211/// After a token is read, this token is fed into the state machine, causing it to transition to a new state or throw an error.
212/// Because the tokens that can be parsed in each state are limited, errors are not actually thrown in the state transition,
213/// due to the fact that erroneous tokens can't be fed into the transition function.
214///
215/// This continues until the string is exhausted, or none of the parsers for the current state can parse the current input.
216pub fn parse(
217    mut i: &str,
218    field_naming_scheme: FieldNamingScheme,
219) -> Result<Vec<RouteParserToken>, PrettyParseError> {
220    let input = i;
221    let mut tokens: Vec<RouteParserToken> = vec![];
222    let mut state = ParserState::None;
223
224    loop {
225        let (ii, token) = parse_impl(i, &state, field_naming_scheme).map_err(|e| match e {
226            nom::Err::Error(e) | nom::Err::Failure(e) => PrettyParseError {
227                error: e,
228                input,
229                remaining: i,
230            },
231            _ => panic!("parser should not be incomplete"),
232        })?;
233        i = ii;
234        state = state.transition(token).map_err(|reason| {
235            let error = ParseError {
236                reason: Some(reason),
237                expected: vec![],
238                offset: 0,
239            };
240            PrettyParseError {
241                error,
242                input,
243                remaining: i,
244            }
245        })?;
246        tokens.push(token);
247
248        // If there is no more input, break out of the loop
249        if i.is_empty() {
250            break;
251        }
252    }
253    Ok(tokens)
254}
255
256fn parse_impl<'a>(
257    i: &'a str,
258    state: &ParserState,
259    field_naming_scheme: FieldNamingScheme,
260) -> IResult<&'a str, RouteParserToken<'a>, ParseError> {
261    match state {
262        ParserState::None => alt((
263            get_slash,
264            get_question,
265            get_and,
266            get_hash,
267            capture(field_naming_scheme),
268            exact,
269            get_end,
270            nothing,
271        ))(i),
272        ParserState::Path { prev_token } => match prev_token {
273            RouteParserToken::Separator => {
274                alt((
275                    exact,
276                    capture(field_naming_scheme),
277                    get_question,
278                    get_hash,
279                    get_end,
280                ))(i)
281                .map_err(|mut e: nom::Err<ParseError>| {
282                    // Detect likely failures if the above failed to match.
283                    let reason: &mut Option<ParserErrorReason> = get_reason(&mut e);
284                    *reason = get_slash(i)
285                        .map(|_| ParserErrorReason::DoubleSlash)
286                        .or_else(|_| get_and(i).map(|_| ParserErrorReason::AndBeforeQuestion))
287                        .ok()
288                        .or(*reason);
289                    e
290                })
291            }
292            RouteParserToken::Exact(_) => {
293                alt((
294                    get_slash,
295                    exact, // This will handle escaped items
296                    capture(field_naming_scheme),
297                    get_question,
298                    get_hash,
299                    get_end,
300                ))(i)
301                .map_err(|mut e: nom::Err<ParseError>| {
302                    // Detect likely failures if the above failed to match.
303                    let reason: &mut Option<ParserErrorReason> = get_reason(&mut e);
304                    *reason = get_and(i)
305                        .map(|_| ParserErrorReason::AndBeforeQuestion)
306                        .ok()
307                        .or(*reason);
308                    e
309                })
310            }
311            RouteParserToken::Capture(_) => {
312                alt((get_slash, exact, get_question, get_hash, get_end))(i).map_err(
313                    |mut e: nom::Err<ParseError>| {
314                        // Detect likely failures if the above failed to match.
315                        let reason: &mut Option<ParserErrorReason> = get_reason(&mut e);
316                        *reason = capture(field_naming_scheme)(i)
317                            .map(|_| ParserErrorReason::AdjacentCaptures)
318                            .or_else(|_| get_and(i).map(|_| ParserErrorReason::AndBeforeQuestion))
319                            .ok()
320                            .or(*reason);
321                        e
322                    },
323                )
324            }
325            _ => Err(nom::Err::Failure(ParseError {
326                reason: Some(ParserErrorReason::InvalidState),
327                expected: vec![],
328                offset: 0,
329            })),
330        },
331        ParserState::FirstQuery { prev_token } => match prev_token {
332            RouteParserToken::QueryBegin => {
333                query(field_naming_scheme)(i).map_err(|mut e: nom::Err<ParseError>| {
334                    // Detect likely failures if the above failed to match.
335                    let reason: &mut Option<ParserErrorReason> = get_reason(&mut e);
336                    *reason = get_question(i)
337                        .map(|_| ParserErrorReason::MultipleQuestions)
338                        .ok()
339                        .or(*reason);
340                    e
341                })
342            }
343            RouteParserToken::Query { .. } => {
344                alt((get_and, get_hash, get_end))(i).map_err(|mut e: nom::Err<ParseError>| {
345                    // Detect likely failures if the above failed to match.
346                    let reason: &mut Option<ParserErrorReason> = get_reason(&mut e);
347                    *reason = get_question(i)
348                        .map(|_| ParserErrorReason::MultipleQuestions)
349                        .ok()
350                        .or(*reason);
351                    e
352                })
353            }
354            _ => Err(nom::Err::Failure(ParseError {
355                reason: Some(ParserErrorReason::InvalidState),
356                expected: vec![],
357                offset: 0,
358            })),
359        },
360        ParserState::NthQuery { prev_token } => match prev_token {
361            RouteParserToken::QuerySeparator => {
362                query(field_naming_scheme)(i).map_err(|mut e: nom::Err<ParseError>| {
363                    // Detect likely failures if the above failed to match.
364                    let reason: &mut Option<ParserErrorReason> = get_reason(&mut e);
365                    *reason = get_question(i)
366                        .map(|_| ParserErrorReason::MultipleQuestions)
367                        .ok()
368                        .or(*reason);
369                    e
370                })
371            }
372            RouteParserToken::Query { .. } => {
373                alt((get_and, get_hash, get_end))(i).map_err(|mut e: nom::Err<ParseError>| {
374                    // Detect likely failures if the above failed to match.
375                    let reason: &mut Option<ParserErrorReason> = get_reason(&mut e);
376                    *reason = get_question(i)
377                        .map(|_| ParserErrorReason::MultipleQuestions)
378                        .ok()
379                        .or(*reason);
380                    e
381                })
382            }
383            _ => Err(nom::Err::Failure(ParseError {
384                reason: Some(ParserErrorReason::InvalidState),
385                expected: vec![],
386                offset: 0,
387            })),
388        },
389        ParserState::Fragment { prev_token } => match prev_token {
390            RouteParserToken::FragmentBegin => {
391                alt((fragment_exact, capture(field_naming_scheme), get_end))(i)
392            }
393            RouteParserToken::Exact(_) => alt((capture(field_naming_scheme), get_end))(i),
394            RouteParserToken::Capture(_) => alt((fragment_exact, get_end))(i),
395            _ => Err(nom::Err::Failure(ParseError {
396                reason: Some(ParserErrorReason::InvalidState),
397                expected: vec![],
398                offset: 0,
399            })),
400        },
401        ParserState::End => Err(nom::Err::Failure(ParseError {
402            reason: Some(ParserErrorReason::TokensAfterEndToken),
403            expected: vec![],
404            offset: 0,
405        })),
406    }
407}
408
409#[cfg(test)]
410mod test {
411    //    use super::*;
412    use super::parse as actual_parse;
413    use crate::{parser::RouteParserToken, FieldNamingScheme, PrettyParseError};
414
415    // Call all tests to parse with the Unnamed variant
416    fn parse(i: &str) -> Result<Vec<RouteParserToken>, PrettyParseError> {
417        actual_parse(i, FieldNamingScheme::Unnamed)
418    }
419
420    mod does_parse {
421        use super::*;
422
423        #[test]
424        fn empty() {
425            let x = parse("").expect("Should parse");
426            assert_eq!(x, vec![RouteParserToken::Nothing])
427        }
428
429        #[test]
430        fn slash() {
431            parse("/").expect("should parse");
432        }
433
434        #[test]
435        fn slash_exact() {
436            parse("/hello").expect("should parse");
437        }
438
439        #[test]
440        fn multiple_exact() {
441            parse("/lorem/ipsum").expect("should parse");
442        }
443
444        #[test]
445        fn capture_in_path() {
446            parse("/lorem/{ipsum}").expect("should parse");
447        }
448
449        #[test]
450        fn capture_rest_in_path() {
451            parse("/lorem/{*:ipsum}").expect("should parse");
452        }
453
454        #[test]
455        fn capture_numbered_in_path() {
456            parse("/lorem/{5:ipsum}").expect("should parse");
457        }
458
459        #[test]
460        fn exact_query_after_path() {
461            parse("/lorem?ipsum=dolor").expect("should parse");
462        }
463
464        #[test]
465        fn leading_query_separator() {
466            parse("&lorem=ipsum").expect("Should parse");
467        }
468
469        #[test]
470        fn exact_query() {
471            parse("?lorem=ipsum").expect("should parse");
472        }
473
474        #[test]
475        fn capture_query() {
476            parse("?lorem={ipsum}").expect("should parse");
477        }
478
479        #[test]
480        fn multiple_queries() {
481            parse("?lorem=ipsum&dolor=sit").expect("should parse");
482        }
483
484        #[test]
485        fn query_and_exact_fragment() {
486            parse("?lorem=ipsum#dolor").expect("should parse");
487        }
488
489        #[test]
490        fn query_with_exact_and_capture_fragment() {
491            parse("?lorem=ipsum#dolor{sit}").expect("should parse");
492        }
493
494        #[test]
495        fn query_with_capture_fragment() {
496            parse("?lorem=ipsum#{dolor}").expect("should parse");
497        }
498
499        #[test]
500        fn escaped_backslash() {
501            let tokens = parse(r#"/escaped\\backslash"#).expect("should parse");
502            let expected = vec![
503                RouteParserToken::Separator,
504                RouteParserToken::Exact(r#"escaped\\backslash"#),
505            ];
506            assert_eq!(tokens, expected);
507        }
508
509        #[test]
510        fn escaped_exclamation() {
511            let tokens = parse(r#"/escaped!!exclamation"#).expect("should parse");
512            let expected = vec![
513                RouteParserToken::Separator,
514                RouteParserToken::Exact(r#"escaped"#),
515                RouteParserToken::Exact(r#"!"#),
516                RouteParserToken::Exact(r#"exclamation"#),
517            ];
518            assert_eq!(tokens, expected);
519        }
520
521        #[test]
522        fn escaped_open_bracket() {
523            let tokens = parse(r#"/escaped{{bracket"#).expect("should parse");
524            let expected = vec![
525                RouteParserToken::Separator,
526                RouteParserToken::Exact(r#"escaped"#),
527                RouteParserToken::Exact(r#"{"#),
528                RouteParserToken::Exact(r#"bracket"#),
529            ];
530            assert_eq!(tokens, expected);
531        }
532
533        #[test]
534        fn escaped_close_bracket() {
535            let tokens = parse(r#"/escaped}}bracket"#).expect("should parse");
536            let expected = vec![
537                RouteParserToken::Separator,
538                RouteParserToken::Exact(r#"escaped"#),
539                RouteParserToken::Exact(r#"}"#),
540                RouteParserToken::Exact(r#"bracket"#),
541            ];
542            assert_eq!(tokens, expected);
543        }
544    }
545
546    mod does_not_parse {
547        use super::*;
548        use crate::error::{ExpectedToken, ParserErrorReason};
549
550        #[test]
551        fn double_slash() {
552            let x = parse("//").expect_err("Should not parse");
553            assert_eq!(x.error.reason, Some(ParserErrorReason::DoubleSlash))
554        }
555
556        #[test]
557        fn slash_ampersand() {
558            let x = parse("/&lorem=ipsum").expect_err("Should not parse");
559            assert_eq!(x.error.reason, Some(ParserErrorReason::AndBeforeQuestion))
560        }
561
562        #[test]
563        fn non_ident_capture() {
564            let x = parse("/{lor#m}").expect_err("Should not parse");
565            assert_eq!(x.error.reason, Some(ParserErrorReason::BadRustIdent('#')));
566            assert_eq!(
567                x.error.expected,
568                vec![ExpectedToken::CloseBracket, ExpectedToken::Ident]
569            )
570        }
571
572        #[test]
573        fn after_end() {
574            let x = parse("/lorem/ipsum!/dolor").expect_err("Should not parse");
575            assert_eq!(x.error.reason, Some(ParserErrorReason::TokensAfterEndToken));
576        }
577    }
578
579    mod correct_parse {
580        use super::*;
581        use crate::parser::{CaptureOrExact, RefCaptureVariant};
582
583        #[test]
584        fn starting_literal() {
585            let parsed = parse("lorem").unwrap();
586            let expected = vec![RouteParserToken::Exact("lorem")];
587            assert_eq!(parsed, expected);
588        }
589
590        #[test]
591        fn minimal_path() {
592            let parsed = parse("/lorem").unwrap();
593            let expected = vec![
594                RouteParserToken::Separator,
595                RouteParserToken::Exact("lorem"),
596            ];
597            assert_eq!(parsed, expected);
598        }
599
600        #[test]
601        fn multiple_path() {
602            let parsed = parse("/lorem/ipsum/dolor/sit").unwrap();
603            let expected = vec![
604                RouteParserToken::Separator,
605                RouteParserToken::Exact("lorem"),
606                RouteParserToken::Separator,
607                RouteParserToken::Exact("ipsum"),
608                RouteParserToken::Separator,
609                RouteParserToken::Exact("dolor"),
610                RouteParserToken::Separator,
611                RouteParserToken::Exact("sit"),
612            ];
613            assert_eq!(parsed, expected);
614        }
615
616        #[test]
617        fn capture_path() {
618            let parsed = parse("/{lorem}/{ipsum}").unwrap();
619            let expected = vec![
620                RouteParserToken::Separator,
621                RouteParserToken::Capture(RefCaptureVariant::Named("lorem")),
622                RouteParserToken::Separator,
623                RouteParserToken::Capture(RefCaptureVariant::Named("ipsum")),
624            ];
625            assert_eq!(parsed, expected);
626        }
627
628        #[test]
629        fn query() {
630            let parsed = parse("?query=this").unwrap();
631            let expected = vec![
632                RouteParserToken::QueryBegin,
633                RouteParserToken::Query {
634                    ident: "query",
635                    capture_or_exact: CaptureOrExact::Exact("this"),
636                },
637            ];
638            assert_eq!(parsed, expected);
639        }
640
641        #[test]
642        fn query_2_part() {
643            let parsed = parse("?lorem=ipsum&dolor=sit").unwrap();
644            let expected = vec![
645                RouteParserToken::QueryBegin,
646                RouteParserToken::Query {
647                    ident: "lorem",
648                    capture_or_exact: CaptureOrExact::Exact("ipsum"),
649                },
650                RouteParserToken::QuerySeparator,
651                RouteParserToken::Query {
652                    ident: "dolor",
653                    capture_or_exact: CaptureOrExact::Exact("sit"),
654                },
655            ];
656            assert_eq!(parsed, expected);
657        }
658
659        #[test]
660        fn query_3_part() {
661            let parsed = parse("?lorem=ipsum&dolor=sit&amet=consectetur").unwrap();
662            let expected = vec![
663                RouteParserToken::QueryBegin,
664                RouteParserToken::Query {
665                    ident: "lorem",
666                    capture_or_exact: CaptureOrExact::Exact("ipsum"),
667                },
668                RouteParserToken::QuerySeparator,
669                RouteParserToken::Query {
670                    ident: "dolor",
671                    capture_or_exact: CaptureOrExact::Exact("sit"),
672                },
673                RouteParserToken::QuerySeparator,
674                RouteParserToken::Query {
675                    ident: "amet",
676                    capture_or_exact: CaptureOrExact::Exact("consectetur"),
677                },
678            ];
679            assert_eq!(parsed, expected);
680        }
681
682        #[test]
683        fn exact_fragment() {
684            let parsed = parse("#lorem").unwrap();
685            let expected = vec![
686                RouteParserToken::FragmentBegin,
687                RouteParserToken::Exact("lorem"),
688            ];
689            assert_eq!(parsed, expected);
690        }
691
692        #[test]
693        fn capture_fragment() {
694            let parsed = parse("#{lorem}").unwrap();
695            let expected = vec![
696                RouteParserToken::FragmentBegin,
697                RouteParserToken::Capture(RefCaptureVariant::Named("lorem")),
698            ];
699            assert_eq!(parsed, expected);
700        }
701
702        #[test]
703        fn mixed_fragment() {
704            let parsed = parse("#{lorem}ipsum{dolor}").unwrap();
705            let expected = vec![
706                RouteParserToken::FragmentBegin,
707                RouteParserToken::Capture(RefCaptureVariant::Named("lorem")),
708                RouteParserToken::Exact("ipsum"),
709                RouteParserToken::Capture(RefCaptureVariant::Named("dolor")),
710            ];
711            assert_eq!(parsed, expected);
712        }
713
714        #[test]
715        fn end_after_path() {
716            let parsed = parse("/lorem!").unwrap();
717            let expected = vec![
718                RouteParserToken::Separator,
719                RouteParserToken::Exact("lorem"),
720                RouteParserToken::End,
721            ];
722            assert_eq!(parsed, expected);
723        }
724
725        #[test]
726        fn end_after_path_separator() {
727            let parsed = parse("/lorem/!").unwrap();
728            let expected = vec![
729                RouteParserToken::Separator,
730                RouteParserToken::Exact("lorem"),
731                RouteParserToken::Separator,
732                RouteParserToken::End,
733            ];
734            assert_eq!(parsed, expected);
735        }
736
737        #[test]
738        fn end_after_path_capture() {
739            let parsed = parse("/lorem/{cap}!").unwrap();
740            let expected = vec![
741                RouteParserToken::Separator,
742                RouteParserToken::Exact("lorem"),
743                RouteParserToken::Separator,
744                RouteParserToken::Capture(RefCaptureVariant::Named("cap")),
745                RouteParserToken::End,
746            ];
747            assert_eq!(parsed, expected);
748        }
749
750        #[test]
751        fn end_after_query_capture() {
752            let parsed = parse("?lorem={cap}!").unwrap();
753            let expected = vec![
754                RouteParserToken::QueryBegin,
755                RouteParserToken::Query {
756                    ident: "lorem",
757                    capture_or_exact: CaptureOrExact::Capture(RefCaptureVariant::Named("cap")),
758                },
759                RouteParserToken::End,
760            ];
761            assert_eq!(parsed, expected);
762        }
763
764        #[test]
765        fn end_after_frag_capture() {
766            let parsed = parse("#{cap}!").unwrap();
767            let expected = vec![
768                RouteParserToken::FragmentBegin,
769                RouteParserToken::Capture(RefCaptureVariant::Named("cap")),
770                RouteParserToken::End,
771            ];
772            assert_eq!(parsed, expected);
773        }
774
775        #[test]
776        fn just_end() {
777            let parsed = parse("!").unwrap();
778            assert_eq!(parsed, vec![RouteParserToken::End]);
779        }
780    }
781}