yew_router_route_parser/
error.rsuse nom::error::ErrorKind;
use std::fmt;
#[derive(Clone, PartialEq)]
pub struct PrettyParseError<'a> {
pub error: ParseError,
pub input: &'a str,
pub remaining: &'a str,
}
fn offset(input: &str, substring: &str) -> usize {
input.len() - substring.len()
}
impl<'a> fmt::Debug for PrettyParseError<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Could not parse route.")?;
f.write_str("\n")?;
let route_str: &str = "Route: ";
f.write_str(route_str)?;
f.write_str(self.input)?;
f.write_str("\n")?;
let offset = offset(self.input, self.remaining);
let offset = offset + self.error.offset;
let pad = (0..offset + route_str.len())
.map(|_| '-')
.collect::<String>();
f.write_str(&format!("{}^", pad))?;
f.write_str("\n")?;
if !self.error.expected.is_empty() {
f.write_str("Expected: ")?;
self.error.expected[..self.error.expected.len() - 1]
.iter()
.try_for_each(|expected| {
<ExpectedToken as fmt::Display>::fmt(expected, f)
.and_then(|_| f.write_str(", "))
})?;
self.error
.expected
.last()
.map(|expected| <ExpectedToken as fmt::Display>::fmt(expected, f))
.transpose()?;
f.write_str("\n")?;
}
if let Some(reason) = self.error.reason {
f.write_str("Reason: ")?;
<ParserErrorReason as fmt::Display>::fmt(&reason, f)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ParseError {
pub reason: Option<ParserErrorReason>,
pub expected: Vec<ExpectedToken>,
pub offset: usize,
}
impl ParseError {
pub(crate) fn expected(expected: ExpectedToken) -> Self {
ParseError {
reason: None,
expected: vec![expected],
offset: 0,
}
}
}
impl nom::error::ParseError<&str> for ParseError {
fn from_error_kind(_input: &str, _kind: ErrorKind) -> Self {
ParseError {
reason: None,
expected: vec![],
offset: 0,
}
}
fn append(_input: &str, _kind: ErrorKind, other: Self) -> Self {
other
}
fn or(mut self, other: Self) -> Self {
self.expected.extend(other.expected);
ParseError {
reason: other.reason.or(self.reason), expected: self.expected,
offset: other.offset, }
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ExpectedToken {
Separator,
Literal,
QueryBegin,
QuerySeparator,
FragmentBegin,
End,
Ident,
OpenBracket,
CloseBracket,
Equals,
Star,
Colon,
}
impl fmt::Display for ExpectedToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExpectedToken::Separator => f.write_str("/"),
ExpectedToken::Literal => f.write_str("<literal>"),
ExpectedToken::QueryBegin => f.write_str("?"),
ExpectedToken::QuerySeparator => f.write_str("&"),
ExpectedToken::FragmentBegin => f.write_str("#"),
ExpectedToken::End => f.write_str("!"),
ExpectedToken::Ident => f.write_str("<ident>"),
ExpectedToken::OpenBracket => f.write_str("{"),
ExpectedToken::CloseBracket => f.write_str("}"),
ExpectedToken::Equals => f.write_str("="),
ExpectedToken::Star => f.write_str("*"),
ExpectedToken::Colon => f.write_str(":"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ParserErrorReason {
TokensAfterEndToken,
DoubleSlash,
EndAfterCapture,
AndBeforeQuestion,
AdjacentCaptures,
MultipleQuestions,
BadRustIdent(char),
BadLiteral,
InvalidState,
CapturesInUnit,
NotAllowedStateTransition,
Expected(ExpectedToken),
}
impl fmt::Display for ParserErrorReason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParserErrorReason::TokensAfterEndToken => {
f.write_str("Characters appeared after the end token (!).")?;
}
ParserErrorReason::DoubleSlash => {
f.write_str("Two slashes are not allowed to be next to each other (//).")?;
}
ParserErrorReason::AndBeforeQuestion => {
f.write_str("The first query must be indicated with a '?', not a '&'.")?;
}
ParserErrorReason::AdjacentCaptures => {
f.write_str("Capture groups can't be next to each other. There must be some character in between the '}' and '{' characters.")?;
}
ParserErrorReason::InvalidState => {
f.write_str("Library Error: The parser was able to enter into an invalid state.")?;
}
ParserErrorReason::NotAllowedStateTransition => {
f.write_str("Library Error: A state transition was attempted that would put the parser in an invalid state")?;
}
ParserErrorReason::MultipleQuestions => {
f.write_str("There can only be one question mark in the query section. `&` should be used to separate other queries.")?;
}
ParserErrorReason::BadRustIdent(c) => {
f.write_str(&format!(
"The character: '{}' could not be used as a Rust identifier.",
c
))?;
}
ParserErrorReason::EndAfterCapture => {
f.write_str("The end token (!) can't appear after a capture ({}).")?;
}
ParserErrorReason::Expected(expected) => {
f.write_str(&format!("Expected: {}", expected))?;
}
ParserErrorReason::BadLiteral => {
f.write_str("Malformed literal.")?;
}
ParserErrorReason::CapturesInUnit => {
f.write_str("Cannot have a capture section for a unit struct or variant.")?;
}
}
Ok(())
}
}
pub(crate) fn get_reason(err: &mut nom::Err<ParseError>) -> &mut Option<ParserErrorReason> {
match err {
nom::Err::Error(err) | nom::Err::Failure(err) => &mut err.reason,
nom::Err::Incomplete(_) => panic!("Incomplete not possible"),
}
}