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