yew_router_min_route_parser/
error.rs

1use nom::error::ErrorKind;
2use std::fmt;
3
4/// Parser error that can print itself in a human-readable format.
5#[derive(Clone, PartialEq)]
6pub struct PrettyParseError<'a> {
7    /// Inner error
8    pub error: ParseError,
9    /// Input to the parser
10    pub input: &'a str,
11    /// Remaining input after partially tokenizing
12    pub remaining: &'a str,
13}
14
15/// Simple offset calculator to determine where to place the carrot for indicating an error.
16fn offset(input: &str, substring: &str) -> usize {
17    input.len() - substring.len()
18}
19
20impl<'a> fmt::Debug for PrettyParseError<'a> {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        f.write_str("Could not parse route.")?;
23        f.write_str("\n")?;
24
25        let route_str: &str = "Route: ";
26        f.write_str(route_str)?;
27        f.write_str(self.input)?;
28        f.write_str("\n")?;
29
30        let offset = offset(self.input, self.remaining);
31        let offset = offset + self.error.offset;
32        let pad = (0..offset + route_str.len())
33            .map(|_| '-')
34            .collect::<String>();
35        f.write_str(&format!("{}^", pad))?;
36        f.write_str("\n")?;
37
38        if !self.error.expected.is_empty() {
39            f.write_str("Expected: ")?;
40            self.error.expected[..self.error.expected.len() - 1]
41                .iter()
42                .map(|expected| {
43                    <ExpectedToken as fmt::Display>::fmt(expected, f)
44                        .and_then(|_| f.write_str(", "))
45                })
46                .collect::<Result<(), fmt::Error>>()?;
47            self.error
48                .expected
49                .last()
50                .map(|expected| <ExpectedToken as fmt::Display>::fmt(expected, f))
51                .transpose()?;
52            f.write_str("\n")?;
53        }
54
55        if let Some(reason) = self.error.reason {
56            f.write_str("Reason: ")?;
57            <ParserErrorReason as fmt::Display>::fmt(&reason, f)?;
58        }
59
60        Ok(())
61    }
62}
63
64/// Error for parsing the route
65#[derive(Debug, Clone, PartialEq)]
66pub struct ParseError {
67    /// A concrete reason why the parse failed.
68    pub reason: Option<ParserErrorReason>,
69    /// Expected token sequences
70    pub expected: Vec<ExpectedToken>,
71    /// Additional offset for failures within sub-parsers.
72    /// Eg. if `{` parses, but then a bad ident is presented, some offset is needed here then.
73    pub offset: usize,
74}
75
76impl ParseError {
77    pub(crate) fn expected(expected: ExpectedToken) -> Self {
78        ParseError {
79            reason: None,
80            expected: vec![expected],
81            offset: 0,
82        }
83    }
84}
85
86impl nom::error::ParseError<&str> for ParseError {
87    fn from_error_kind(_input: &str, _kind: ErrorKind) -> Self {
88        ParseError {
89            reason: None,
90            expected: vec![],
91            offset: 0,
92        }
93    }
94
95    fn append(_input: &str, _kind: ErrorKind, other: Self) -> Self {
96        other
97    }
98
99    fn or(mut self, other: Self) -> Self {
100        // It is assumed that there aren't duplicates.
101        self.expected.extend(other.expected);
102
103        ParseError {
104            reason: other.reason.or(self.reason), // Take the right most reason
105            expected: self.expected,
106            offset: other.offset, /* Defer to the "other"'s offset. TODO it might make sense if the offsets are different, only show the other's "expected". */
107        }
108    }
109}
110
111#[derive(Debug, Clone, Copy, PartialEq)]
112pub enum ExpectedToken {
113    ///  /
114    Separator,
115    /// specific string.
116    Literal,
117    ///  ?
118    QueryBegin,
119    ///  &
120    QuerySeparator,
121    /// \#
122    FragmentBegin,
123    /// !
124    End,
125    /// identifier within {}
126    Ident,
127    /// {
128    OpenBracket,
129    /// }
130    CloseBracket,
131    /// =
132    Equals,
133    /// *
134    Star,
135    /// :
136    Colon,
137}
138
139impl fmt::Display for ExpectedToken {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        match self {
142            ExpectedToken::Separator => f.write_str("/"),
143            ExpectedToken::Literal => f.write_str("<literal>"),
144            ExpectedToken::QueryBegin => f.write_str("?"),
145            ExpectedToken::QuerySeparator => f.write_str("&"),
146            ExpectedToken::FragmentBegin => f.write_str("#"),
147            ExpectedToken::End => f.write_str("!"),
148            ExpectedToken::Ident => f.write_str("<ident>"),
149            ExpectedToken::OpenBracket => f.write_str("{"),
150            ExpectedToken::CloseBracket => f.write_str("}"),
151            ExpectedToken::Equals => f.write_str("="),
152            ExpectedToken::Star => f.write_str("*"),
153            ExpectedToken::Colon => f.write_str(":"),
154        }
155    }
156}
157
158/// A concrete reason why a parse failed
159#[derive(Debug, Clone, Copy, PartialEq)]
160pub enum ParserErrorReason {
161    /// Some token encountered after the end token.
162    TokensAfterEndToken,
163    /// Two slashes are able to occur next to each other.
164    DoubleSlash,
165    /// End after a {}
166    EndAfterCapture,
167    /// A & appears before a ?
168    AndBeforeQuestion,
169    /// Captures can't be next to each other
170    AdjacentCaptures,
171    /// There can only be one question mark in the query section
172    MultipleQuestions,
173    /// The provided ident within a capture group could never match with a valid rust identifier.
174    BadRustIdent(char),
175    /// A bad literal.
176    BadLiteral,
177    /// Invalid state
178    InvalidState,
179    /// Can't have capture sections for unit structs/variants
180    CapturesInUnit,
181    /// Internal check on valid state transitions
182    /// This should never actually be created.
183    NotAllowedStateTransition,
184    /// Expected a specific token
185    Expected(ExpectedToken),
186}
187
188impl fmt::Display for ParserErrorReason {
189    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190        match self {
191            ParserErrorReason::TokensAfterEndToken => {
192                f.write_str("Characters appeared after the end token (!).")?;
193            }
194            ParserErrorReason::DoubleSlash => {
195                f.write_str("Two slashes are not allowed to be next to each other (//).")?;
196            }
197            ParserErrorReason::AndBeforeQuestion => {
198                f.write_str("The first query must be indicated with a '?', not a '&'.")?;
199            }
200            ParserErrorReason::AdjacentCaptures => {
201                f.write_str("Capture groups can't be next to each other. There must be some character in between the '}' and '{' characters.")?;
202            }
203            ParserErrorReason::InvalidState => {
204                f.write_str("Library Error: The parser was able to enter into an invalid state.")?;
205            }
206            ParserErrorReason::NotAllowedStateTransition => {
207                f.write_str("Library Error: A state transition was attempted that would put the parser in an invalid state")?;
208            }
209            ParserErrorReason::MultipleQuestions => {
210                f.write_str("There can only be one question mark in the query section. `&` should be used to separate other queries.")?;
211            }
212            ParserErrorReason::BadRustIdent(c) => {
213                f.write_str(&format!(
214                    "The character: '{}' could not be used as a Rust identifier.",
215                    c
216                ))?;
217            }
218            ParserErrorReason::EndAfterCapture => {
219                f.write_str("The end token (!) can't appear after a capture ({}).")?;
220            }
221            ParserErrorReason::Expected(expected) => {
222                f.write_str(&format!("Expected: {}", expected))?;
223            }
224            ParserErrorReason::BadLiteral => {
225                f.write_str("Malformed literal.")?;
226            }
227            ParserErrorReason::CapturesInUnit => {
228                f.write_str("Cannot have a capture section for a unit struct or variant.")?;
229            }
230        }
231        Ok(())
232    }
233}
234
235pub(crate) fn get_reason(err: &mut nom::Err<ParseError>) -> &mut Option<ParserErrorReason> {
236    match err {
237        nom::Err::Error(err) | nom::Err::Failure(err) => &mut err.reason,
238        nom::Err::Incomplete(_) => panic!("Incomplete not possible"),
239    }
240}