utils/parser/
error.rs

1use crate::ascii::AsciiSet;
2use crate::parser::then::Then2;
3use crate::parser::{ParseState, Parser, ParserResult};
4use std::error::Error;
5use std::fmt::{Display, Formatter};
6
7/// Error type returned by parsers.
8///
9/// Returned by both [`Parser::parse_ctx`] and [`Leaf::parse`](super::Leaf::parse).
10#[non_exhaustive]
11#[derive(Debug, Copy, Clone, PartialEq, Eq)]
12pub enum ParseError {
13    /// Expected $type.
14    Expected(&'static str),
15    /// Expected $literal.
16    ExpectedLiteral(&'static str),
17    /// Expected $byte.
18    ExpectedByte(u8),
19    /// Expected $min - $max.
20    ExpectedByteRange(u8, u8),
21    /// Expected one of $set.
22    ExpectedOneOf(AsciiSet),
23    /// Expected at least $n matches.
24    ExpectedMatches(usize),
25    /// Expected $n items or less.
26    ExpectedLessItems(usize),
27    /// Expected end of input.
28    ExpectedEof(),
29    /// Expected number <= $num.
30    NumberTooLarge(i128),
31    /// Expected number >= $num.
32    NumberTooSmall(i128),
33    /// Number out of range.
34    ///
35    /// Used as a fallback if min/max bound doesn't fit in an [`i128`] (for example, [`u128::MAX`]).
36    NumberOutOfRange(),
37    /// Custom error returned by [`Parser::map_res`] & [`Parser::error_msg`].
38    Custom(&'static str),
39}
40
41impl ParseError {
42    #[inline]
43    pub(super) fn too_large(max: impl TryInto<i128>) -> Self {
44        if let Ok(max) = max.try_into() {
45            Self::NumberTooLarge(max)
46        } else {
47            Self::NumberOutOfRange()
48        }
49    }
50
51    #[inline]
52    pub(super) fn too_small(min: impl TryInto<i128>) -> Self {
53        if let Ok(min) = min.try_into() {
54            Self::NumberTooSmall(min)
55        } else {
56            Self::NumberOutOfRange()
57        }
58    }
59}
60
61impl Display for ParseError {
62    #[cold]
63    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
64        match *self {
65            ParseError::Expected(x) => write!(f, "expected {x}"),
66            ParseError::ExpectedLiteral(x) => write!(f, "expected {x:?}"),
67            ParseError::ExpectedByte(x) => write!(f, "expected {:?}", x as char),
68            ParseError::ExpectedByteRange(min, max) => {
69                write!(f, "expected {:?}-{:?}", min as char, max as char)
70            }
71            ParseError::ExpectedOneOf(set) => write!(f, "expected one of {set}"),
72            ParseError::ExpectedEof() => write!(f, "expected end of input"),
73            ParseError::ExpectedMatches(x) => write!(f, "expected at least {x} match"),
74            ParseError::ExpectedLessItems(x) => write!(f, "expected {x} items or less"),
75            ParseError::NumberTooLarge(x) => write!(f, "expected number <= {x}"),
76            ParseError::NumberTooSmall(x) => write!(f, "expected number >= {x}"),
77            ParseError::NumberOutOfRange() => write!(f, "number out of range"),
78            ParseError::Custom(x) => f.write_str(x),
79        }
80    }
81}
82
83impl Error for ParseError {}
84
85impl PartialEq<ParseError> for Box<dyn Error> {
86    fn eq(&self, other: &ParseError) -> bool {
87        if let Some(pe) = self.downcast_ref::<ParseError>() {
88            pe == other
89        } else {
90            false
91        }
92    }
93}
94impl PartialEq<Box<dyn Error>> for ParseError {
95    fn eq(&self, other: &Box<dyn Error>) -> bool {
96        other == self
97    }
98}
99
100#[derive(Copy, Clone)]
101pub struct WithErrorMsg<P> {
102    pub(super) parser: P,
103    pub(super) message: &'static str,
104}
105impl<'i, P: Parser<'i>> Parser<'i> for WithErrorMsg<P> {
106    type Output = P::Output;
107    type Then<T: Parser<'i>> = Then2<Self, T>;
108
109    #[inline]
110    fn parse_ctx(
111        &self,
112        input: &'i [u8],
113        state: &mut ParseState<'i>,
114        commit: &mut bool,
115        tail: bool,
116    ) -> ParserResult<'i, Self::Output> {
117        let prev_remaining = state.error.map(|e| e.1);
118        self.parser
119            .parse_ctx(input, state, commit, tail)
120            .inspect_err(|_| {
121                let remaining = state.error.unwrap().1;
122                if prev_remaining != Some(remaining) {
123                    // If the error location has changed, update the stored message
124                    state.error = Some((ParseError::Custom(self.message), remaining));
125                }
126            })
127    }
128}