Skip to main content

winnow/
error.rs

1//! # Error management
2//!
3//! Errors are designed with multiple needs in mind:
4//! - Accumulate more [context][Parser::context] as the error goes up the parser chain
5//! - Distinguish between [recoverable errors,
6//!   unrecoverable errors, and more data is needed][ErrMode]
7//! - Have a very low overhead, as errors are often discarded by the calling parser (examples: `repeat`, `alt`)
8//! - Can be modified according to the user's needs, because some languages need a lot more information
9//! - Help thread-through the [stream][crate::stream]
10//!
11//! To abstract these needs away from the user, generally `winnow` parsers use the [`ModalResult`]
12//! alias, rather than [`Result`].  [`Parser::parse`] is a top-level operation
13//! that can help convert to a `Result` for integrating with your application's error reporting.
14//!
15//! Error types include:
16//! - [`EmptyError`] when the reason for failure doesn't matter
17//! - [`ContextError`]
18//! - [`InputError`] (mostly for testing)
19//! - [`TreeError`] (mostly for testing)
20//! - [Custom errors][crate::_topic::error]
21
22#[cfg(feature = "alloc")]
23use alloc::borrow::ToOwned;
24use core::fmt;
25
26use crate::stream::AsBStr;
27use crate::stream::Stream;
28#[allow(unused_imports)] // Here for intra-doc links
29use crate::Parser;
30
31pub use crate::stream::Needed;
32
33/// By default, the error type (`E`) is [`ContextError`].
34///
35/// When integrating into the result of the application, see
36/// - [`Parser::parse`]
37/// - [`ParserError::into_inner`]
38pub type Result<O, E = ContextError> = core::result::Result<O, E>;
39
40/// [Modal error reporting][ErrMode] for [`Parser::parse_next`]
41///
42/// - `Ok(O)` is the parsed value
43/// - [`Err(ErrMode<E>)`][ErrMode] is the error along with how to respond to it
44///
45/// By default, the error type (`E`) is [`ContextError`].
46///
47/// When integrating into the result of the application, see
48/// - [`Parser::parse`]
49/// - [`ParserError::into_inner`]
50pub type ModalResult<O, E = ContextError> = Result<O, ErrMode<E>>;
51
52#[cfg(test)]
53pub(crate) type TestResult<I, O> = ModalResult<O, InputError<I>>;
54
55/// Add parse error state to [`ParserError`]s
56///
57/// Needed for
58/// - [`Partial`][crate::stream::Partial] to track whether the [`Stream`] is [`ErrMode::Incomplete`].
59///   See also [`crate::_topic::partial`]
60/// - Marking errors as unrecoverable ([`ErrMode::Cut`]) and not retrying alternative parsers.
61///   See also [`crate::_tutorial::chapter_7#error-cuts`]
62#[derive(Debug, Clone, PartialEq)]
63pub enum ErrMode<E> {
64    /// There was not enough data to determine the appropriate action
65    ///
66    /// More data needs to be buffered before retrying the parse.
67    ///
68    /// This must only be set when the [`Stream`] is [partial][`crate::stream::StreamIsPartial`], like with
69    /// [`Partial`][crate::Partial]
70    ///
71    /// Convert this into an `Backtrack` with [`Parser::complete_err`]
72    Incomplete(Needed),
73    /// The parser failed with a recoverable error (the default).
74    ///
75    /// For example, a parser for json values might include a
76    /// [`dec_uint`][crate::ascii::dec_uint] as one case in an [`alt`][crate::combinator::alt]
77    /// combinator. If it fails, the next case should be tried.
78    Backtrack(E),
79    /// The parser had an unrecoverable error.
80    ///
81    /// The parser was on the right branch, so directly report it to the user rather than trying
82    /// other branches. You can use [`cut_err()`][crate::combinator::cut_err] combinator to switch
83    /// from `ErrMode::Backtrack` to `ErrMode::Cut`.
84    ///
85    /// For example, one case in an [`alt`][crate::combinator::alt] combinator found a unique prefix
86    /// and you want any further errors parsing the case to be reported to the user.
87    Cut(E),
88}
89
90impl<E> ErrMode<E> {
91    /// Tests if the result is Incomplete
92    #[inline]
93    pub fn is_incomplete(&self) -> bool {
94        matches!(self, ErrMode::Incomplete(_))
95    }
96
97    /// Prevent backtracking, bubbling the error up to the top
98    pub fn cut(self) -> Self {
99        match self {
100            ErrMode::Backtrack(e) => ErrMode::Cut(e),
101            rest => rest,
102        }
103    }
104
105    /// Enable backtracking support
106    pub fn backtrack(self) -> Self {
107        match self {
108            ErrMode::Cut(e) => ErrMode::Backtrack(e),
109            rest => rest,
110        }
111    }
112
113    /// Applies the given function to the inner error
114    pub fn map<E2, F>(self, f: F) -> ErrMode<E2>
115    where
116        F: FnOnce(E) -> E2,
117    {
118        match self {
119            ErrMode::Incomplete(n) => ErrMode::Incomplete(n),
120            ErrMode::Cut(t) => ErrMode::Cut(f(t)),
121            ErrMode::Backtrack(t) => ErrMode::Backtrack(f(t)),
122        }
123    }
124
125    /// Automatically converts between errors if the underlying type supports it
126    pub fn convert<F>(self) -> ErrMode<F>
127    where
128        E: ErrorConvert<F>,
129    {
130        ErrorConvert::convert(self)
131    }
132
133    /// Unwrap the mode, returning the underlying error
134    ///
135    /// Returns `Err(self)` for [`ErrMode::Incomplete`]
136    #[inline(always)]
137    pub fn into_inner(self) -> Result<E, Self> {
138        match self {
139            ErrMode::Backtrack(e) | ErrMode::Cut(e) => Ok(e),
140            err @ ErrMode::Incomplete(_) => Err(err),
141        }
142    }
143}
144
145impl<I: Stream, E: ParserError<I>> ParserError<I> for ErrMode<E> {
146    type Inner = E;
147
148    #[inline(always)]
149    fn from_input(input: &I) -> Self {
150        ErrMode::Backtrack(E::from_input(input))
151    }
152
153    #[inline(always)]
154    fn assert(input: &I, message: &'static str) -> Self
155    where
156        I: core::fmt::Debug,
157    {
158        ErrMode::Cut(E::assert(input, message))
159    }
160
161    #[inline(always)]
162    fn incomplete(_input: &I, needed: Needed) -> Self {
163        ErrMode::Incomplete(needed)
164    }
165
166    #[inline]
167    fn append(self, input: &I, token_start: &<I as Stream>::Checkpoint) -> Self {
168        match self {
169            ErrMode::Backtrack(e) => ErrMode::Backtrack(e.append(input, token_start)),
170            e => e,
171        }
172    }
173
174    fn or(self, other: Self) -> Self {
175        match (self, other) {
176            (ErrMode::Backtrack(e), ErrMode::Backtrack(o)) => ErrMode::Backtrack(e.or(o)),
177            (ErrMode::Incomplete(e), _) | (_, ErrMode::Incomplete(e)) => ErrMode::Incomplete(e),
178            (ErrMode::Cut(e), _) | (_, ErrMode::Cut(e)) => ErrMode::Cut(e),
179        }
180    }
181
182    #[inline(always)]
183    fn is_backtrack(&self) -> bool {
184        matches!(self, ErrMode::Backtrack(_))
185    }
186
187    #[inline(always)]
188    fn into_inner(self) -> Result<Self::Inner, Self> {
189        match self {
190            ErrMode::Backtrack(e) | ErrMode::Cut(e) => Ok(e),
191            err @ ErrMode::Incomplete(_) => Err(err),
192        }
193    }
194
195    #[inline(always)]
196    fn is_incomplete(&self) -> bool {
197        matches!(self, ErrMode::Incomplete(_))
198    }
199
200    #[inline(always)]
201    fn needed(&self) -> Option<Needed> {
202        match self {
203            ErrMode::Incomplete(needed) => Some(*needed),
204            _ => None,
205        }
206    }
207}
208
209impl<E> ModalError for ErrMode<E> {
210    fn cut(self) -> Self {
211        self.cut()
212    }
213
214    fn backtrack(self) -> Self {
215        self.backtrack()
216    }
217}
218
219impl<E1, E2> ErrorConvert<ErrMode<E2>> for ErrMode<E1>
220where
221    E1: ErrorConvert<E2>,
222{
223    #[inline(always)]
224    fn convert(self) -> ErrMode<E2> {
225        self.map(|e| e.convert())
226    }
227}
228
229impl<I, EXT, E> FromExternalError<I, EXT> for ErrMode<E>
230where
231    E: FromExternalError<I, EXT>,
232{
233    #[inline(always)]
234    fn from_external_error(input: &I, e: EXT) -> Self {
235        ErrMode::Backtrack(E::from_external_error(input, e))
236    }
237}
238
239impl<I: Stream, C, E: AddContext<I, C>> AddContext<I, C> for ErrMode<E> {
240    #[inline(always)]
241    fn add_context(self, input: &I, token_start: &<I as Stream>::Checkpoint, context: C) -> Self {
242        self.map(|err| err.add_context(input, token_start, context))
243    }
244}
245
246#[cfg(feature = "unstable-recover")]
247#[cfg(feature = "std")]
248impl<I: Stream, E1: FromRecoverableError<I, E2>, E2> FromRecoverableError<I, ErrMode<E2>>
249    for ErrMode<E1>
250{
251    #[inline]
252    fn from_recoverable_error(
253        token_start: &<I as Stream>::Checkpoint,
254        err_start: &<I as Stream>::Checkpoint,
255        input: &I,
256        e: ErrMode<E2>,
257    ) -> Self {
258        e.map(|e| E1::from_recoverable_error(token_start, err_start, input, e))
259    }
260}
261
262impl<T: Clone> ErrMode<InputError<T>> {
263    /// Maps `ErrMode<InputError<T>>` to `ErrMode<InputError<U>>` with the given `F: T -> U`
264    pub fn map_input<U: Clone, F>(self, f: F) -> ErrMode<InputError<U>>
265    where
266        F: FnOnce(T) -> U,
267    {
268        match self {
269            ErrMode::Incomplete(n) => ErrMode::Incomplete(n),
270            ErrMode::Cut(InputError { input }) => ErrMode::Cut(InputError { input: f(input) }),
271            ErrMode::Backtrack(InputError { input }) => {
272                ErrMode::Backtrack(InputError { input: f(input) })
273            }
274        }
275    }
276}
277
278impl<E: Eq> Eq for ErrMode<E> {}
279
280impl<E> fmt::Display for ErrMode<E>
281where
282    E: fmt::Debug,
283{
284    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285        match self {
286            ErrMode::Incomplete(Needed::Size(u)) => write!(f, "Parsing requires {u} more data"),
287            ErrMode::Incomplete(Needed::Unknown) => write!(f, "Parsing requires more data"),
288            ErrMode::Cut(c) => write!(f, "Parsing Failure: {c:?}"),
289            ErrMode::Backtrack(c) => write!(f, "Parsing Error: {c:?}"),
290        }
291    }
292}
293
294/// The basic [`Parser`] trait for errors
295///
296/// It provides methods to create an error from some combinators,
297/// and combine existing errors in combinators like `alt`.
298pub trait ParserError<I: Stream>: Sized {
299    /// Generally, `Self`
300    ///
301    /// Mostly used for [`ErrMode`]
302    type Inner;
303
304    /// Creates an error from the input position
305    fn from_input(input: &I) -> Self;
306
307    /// Process a parser assertion
308    #[inline(always)]
309    fn assert(input: &I, _message: &'static str) -> Self
310    where
311        I: core::fmt::Debug,
312    {
313        #[cfg(debug_assertions)]
314        panic!("assert `{_message}` failed at {input:#?}");
315        #[cfg(not(debug_assertions))]
316        Self::from_input(input)
317    }
318
319    /// There was not enough data to determine the appropriate action
320    ///
321    /// More data needs to be buffered before retrying the parse.
322    ///
323    /// This must only be set when the [`Stream`] is [partial][`crate::stream::StreamIsPartial`], like with
324    /// [`Partial`][crate::Partial]
325    ///
326    /// Convert this into an `Backtrack` with [`Parser::complete_err`]
327    #[inline(always)]
328    fn incomplete(input: &I, _needed: Needed) -> Self {
329        Self::from_input(input)
330    }
331
332    /// Like [`ParserError::from_input`] but merges it with the existing error.
333    ///
334    /// This is useful when backtracking through a parse tree, accumulating error context on the
335    /// way.
336    #[inline]
337    fn append(self, _input: &I, _token_start: &<I as Stream>::Checkpoint) -> Self {
338        self
339    }
340
341    /// Combines errors from two different parse branches.
342    ///
343    /// For example, this would be used by [`alt`][crate::combinator::alt] to report the error from
344    /// each case.
345    #[inline]
346    fn or(self, other: Self) -> Self {
347        other
348    }
349
350    /// Is backtracking and trying new parse branches allowed?
351    #[inline(always)]
352    fn is_backtrack(&self) -> bool {
353        true
354    }
355
356    /// Unwrap the mode, returning the underlying error, if present
357    fn into_inner(self) -> Result<Self::Inner, Self>;
358
359    /// Is more data [`Needed`]
360    ///
361    /// This must be the same as [`err.needed().is_some()`][ParserError::needed]
362    #[inline(always)]
363    fn is_incomplete(&self) -> bool {
364        false
365    }
366
367    /// Extract the [`Needed`] data, if present
368    ///
369    /// `Self::needed().is_some()` must be the same as
370    /// [`err.is_incomplete()`][ParserError::is_incomplete]
371    #[inline(always)]
372    fn needed(&self) -> Option<Needed> {
373        None
374    }
375}
376
377/// Manipulate the how parsers respond to this error
378pub trait ModalError {
379    /// Prevent backtracking, bubbling the error up to the top
380    fn cut(self) -> Self;
381    /// Enable backtracking support
382    fn backtrack(self) -> Self;
383}
384
385/// Used by [`Parser::context`] to add custom data to error while backtracking
386///
387/// May be implemented multiple times for different kinds of context.
388pub trait AddContext<I: Stream, C = &'static str>: Sized {
389    /// Append to an existing error custom data
390    ///
391    /// This is used mainly by [`Parser::context`], to add user friendly information
392    /// to errors when backtracking through a parse tree
393    #[inline]
394    fn add_context(
395        self,
396        _input: &I,
397        _token_start: &<I as Stream>::Checkpoint,
398        _context: C,
399    ) -> Self {
400        self
401    }
402}
403
404/// Capture context from when an error was recovered
405#[cfg(feature = "unstable-recover")]
406#[cfg(feature = "std")]
407pub trait FromRecoverableError<I: Stream, E> {
408    /// Capture context from when an error was recovered
409    fn from_recoverable_error(
410        token_start: &<I as Stream>::Checkpoint,
411        err_start: &<I as Stream>::Checkpoint,
412        input: &I,
413        e: E,
414    ) -> Self;
415}
416
417/// Create a new error with an external error, from [`std::str::FromStr`]
418///
419/// This trait is required by the [`Parser::try_map`] combinator.
420pub trait FromExternalError<I, E> {
421    /// Like [`ParserError::from_input`] but also include an external error.
422    fn from_external_error(input: &I, e: E) -> Self;
423}
424
425/// Equivalent of `From` implementation to avoid orphan rules in bits parsers
426pub trait ErrorConvert<E> {
427    /// Transform to another error type
428    fn convert(self) -> E;
429}
430
431/// Capture input on error
432///
433/// This is useful for testing of generic parsers to ensure the error happens at the right
434/// location.
435///
436/// <div class="warning">
437///
438/// **Note:** [context][Parser::context] and inner errors (like from [`Parser::try_map`]) will be
439/// dropped.
440///
441/// </div>
442#[derive(Copy, Clone, Debug, Eq, PartialEq)]
443pub struct InputError<I: Clone> {
444    /// The input stream, pointing to the location where the error occurred
445    pub input: I,
446}
447
448impl<I: Clone> InputError<I> {
449    /// Creates a new basic error
450    #[inline]
451    pub fn at(input: I) -> Self {
452        Self { input }
453    }
454
455    /// Translate the input type
456    #[inline]
457    pub fn map_input<I2: Clone, O: Fn(I) -> I2>(self, op: O) -> InputError<I2> {
458        InputError {
459            input: op(self.input),
460        }
461    }
462}
463
464#[cfg(feature = "alloc")]
465impl<I: ToOwned> InputError<&I>
466where
467    <I as ToOwned>::Owned: Clone,
468{
469    /// Obtaining ownership
470    pub fn into_owned(self) -> InputError<<I as ToOwned>::Owned> {
471        self.map_input(ToOwned::to_owned)
472    }
473}
474
475impl<I: Stream + Clone> ParserError<I> for InputError<I> {
476    type Inner = Self;
477
478    #[inline]
479    fn from_input(input: &I) -> Self {
480        Self {
481            input: input.clone(),
482        }
483    }
484
485    #[inline(always)]
486    fn into_inner(self) -> Result<Self::Inner, Self> {
487        Ok(self)
488    }
489}
490
491impl<I: Stream + Clone, C> AddContext<I, C> for InputError<I> {}
492
493#[cfg(feature = "unstable-recover")]
494#[cfg(feature = "std")]
495impl<I: Clone + Stream> FromRecoverableError<I, Self> for InputError<I> {
496    #[inline]
497    fn from_recoverable_error(
498        _token_start: &<I as Stream>::Checkpoint,
499        _err_start: &<I as Stream>::Checkpoint,
500        _input: &I,
501        e: Self,
502    ) -> Self {
503        e
504    }
505}
506
507impl<I: Clone, E> FromExternalError<I, E> for InputError<I> {
508    /// Create a new error from an input position and an external error
509    #[inline]
510    fn from_external_error(input: &I, _e: E) -> Self {
511        Self {
512            input: input.clone(),
513        }
514    }
515}
516
517/// The Display implementation allows the `std::error::Error` implementation
518impl<I: Clone + fmt::Display> fmt::Display for InputError<I> {
519    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
520        write!(f, "failed to parse starting at: {}", self.input)
521    }
522}
523
524#[cfg(feature = "std")]
525impl<I: Clone + fmt::Debug + fmt::Display + Sync + Send + 'static> std::error::Error
526    for InputError<I>
527{
528}
529
530/// Track an error occurred without any other [`StrContext`]
531#[derive(Copy, Clone, Debug, Eq, PartialEq)]
532pub struct EmptyError;
533
534impl<I: Stream> ParserError<I> for EmptyError {
535    type Inner = Self;
536
537    #[inline(always)]
538    fn from_input(_: &I) -> Self {
539        Self
540    }
541
542    #[inline(always)]
543    fn into_inner(self) -> Result<Self::Inner, Self> {
544        Ok(self)
545    }
546}
547
548impl<I: Stream, C> AddContext<I, C> for EmptyError {}
549
550#[cfg(feature = "unstable-recover")]
551#[cfg(feature = "std")]
552impl<I: Stream> FromRecoverableError<I, Self> for EmptyError {
553    #[inline(always)]
554    fn from_recoverable_error(
555        _token_start: &<I as Stream>::Checkpoint,
556        _err_start: &<I as Stream>::Checkpoint,
557        _input: &I,
558        e: Self,
559    ) -> Self {
560        e
561    }
562}
563
564impl<I, E> FromExternalError<I, E> for EmptyError {
565    #[inline(always)]
566    fn from_external_error(_input: &I, _e: E) -> Self {
567        Self
568    }
569}
570
571impl ErrorConvert<EmptyError> for EmptyError {
572    #[inline(always)]
573    fn convert(self) -> EmptyError {
574        self
575    }
576}
577
578impl core::fmt::Display for EmptyError {
579    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
580        "failed to parse".fmt(f)
581    }
582}
583
584impl<I: Stream> ParserError<I> for () {
585    type Inner = Self;
586
587    #[inline]
588    fn from_input(_: &I) -> Self {}
589
590    #[inline(always)]
591    fn into_inner(self) -> Result<Self::Inner, Self> {
592        Ok(self)
593    }
594}
595
596impl<I: Stream, C> AddContext<I, C> for () {}
597
598#[cfg(feature = "unstable-recover")]
599#[cfg(feature = "std")]
600impl<I: Stream> FromRecoverableError<I, Self> for () {
601    #[inline]
602    fn from_recoverable_error(
603        _token_start: &<I as Stream>::Checkpoint,
604        _err_start: &<I as Stream>::Checkpoint,
605        _input: &I,
606        (): Self,
607    ) -> Self {
608    }
609}
610
611impl<I, E> FromExternalError<I, E> for () {
612    #[inline]
613    fn from_external_error(_input: &I, _e: E) -> Self {}
614}
615
616impl ErrorConvert<()> for () {
617    #[inline]
618    fn convert(self) {}
619}
620
621/// Accumulate context while backtracking errors
622///
623/// See the [tutorial][crate::_tutorial::chapter_7#error-adaptation-and-rendering]
624/// for an example of how to adapt this to an application error with custom rendering.
625#[derive(Debug)]
626pub struct ContextError<C = StrContext> {
627    #[cfg(feature = "alloc")]
628    context: alloc::vec::Vec<C>,
629    #[cfg(not(feature = "alloc"))]
630    context: core::marker::PhantomData<C>,
631    #[cfg(feature = "std")]
632    cause: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
633}
634
635impl<C> ContextError<C> {
636    /// Create an empty error
637    #[inline]
638    pub fn new() -> Self {
639        Self {
640            context: Default::default(),
641            #[cfg(feature = "std")]
642            cause: None,
643        }
644    }
645
646    /// Add more context
647    #[inline]
648    pub fn push(&mut self, context: C) {
649        #[cfg(feature = "alloc")]
650        self.context.push(context);
651    }
652
653    /// Add more context
654    #[inline]
655    pub fn extend<I: IntoIterator<Item = C>>(&mut self, context: I) {
656        #[cfg(feature = "alloc")]
657        self.context.extend(context);
658    }
659
660    /// Access context from [`Parser::context`]
661    #[inline]
662    #[cfg(feature = "alloc")]
663    pub fn context(&self) -> impl Iterator<Item = &C> {
664        self.context.iter()
665    }
666
667    /// Originating [`std::error::Error`]
668    #[inline]
669    #[cfg(feature = "std")]
670    pub fn cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
671        self.cause.as_deref()
672    }
673}
674
675impl<C: Clone> Clone for ContextError<C> {
676    fn clone(&self) -> Self {
677        Self {
678            context: self.context.clone(),
679            #[cfg(feature = "std")]
680            cause: self.cause.as_ref().map(|e| e.to_string().into()),
681        }
682    }
683}
684
685impl<C> Default for ContextError<C> {
686    #[inline]
687    fn default() -> Self {
688        Self::new()
689    }
690}
691
692impl<I: Stream, C> ParserError<I> for ContextError<C> {
693    type Inner = Self;
694
695    #[inline]
696    fn from_input(_input: &I) -> Self {
697        Self::new()
698    }
699
700    #[inline(always)]
701    fn into_inner(self) -> Result<Self::Inner, Self> {
702        Ok(self)
703    }
704}
705
706impl<C, I: Stream> AddContext<I, C> for ContextError<C> {
707    #[inline]
708    fn add_context(
709        mut self,
710        _input: &I,
711        _token_start: &<I as Stream>::Checkpoint,
712        context: C,
713    ) -> Self {
714        self.push(context);
715        self
716    }
717}
718
719#[cfg(feature = "unstable-recover")]
720#[cfg(feature = "std")]
721impl<I: Stream, C> FromRecoverableError<I, Self> for ContextError<C> {
722    #[inline]
723    fn from_recoverable_error(
724        _token_start: &<I as Stream>::Checkpoint,
725        _err_start: &<I as Stream>::Checkpoint,
726        _input: &I,
727        e: Self,
728    ) -> Self {
729        e
730    }
731}
732
733#[cfg(feature = "std")]
734impl<C, I, E: std::error::Error + Send + Sync + 'static> FromExternalError<I, E>
735    for ContextError<C>
736{
737    #[inline]
738    fn from_external_error(_input: &I, e: E) -> Self {
739        let mut err = Self::new();
740        {
741            err.cause = Some(Box::new(e));
742        }
743        err
744    }
745}
746
747// HACK: This is more general than `std`, making the features non-additive
748#[cfg(not(feature = "std"))]
749impl<C, I, E: Send + Sync + 'static> FromExternalError<I, E> for ContextError<C> {
750    #[inline]
751    fn from_external_error(_input: &I, _e: E) -> Self {
752        let err = Self::new();
753        err
754    }
755}
756
757// For tests
758impl<C: core::cmp::PartialEq> core::cmp::PartialEq for ContextError<C> {
759    fn eq(&self, other: &Self) -> bool {
760        #[cfg(feature = "alloc")]
761        {
762            if self.context != other.context {
763                return false;
764            }
765        }
766        #[cfg(feature = "std")]
767        {
768            if self.cause.as_ref().map(ToString::to_string)
769                != other.cause.as_ref().map(ToString::to_string)
770            {
771                return false;
772            }
773        }
774
775        true
776    }
777}
778
779impl core::fmt::Display for ContextError<StrContext> {
780    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
781        #[cfg(feature = "alloc")]
782        {
783            let expression = self.context().find_map(|c| match c {
784                StrContext::Label(c) => Some(c),
785                _ => None,
786            });
787            let expected = self
788                .context()
789                .filter_map(|c| match c {
790                    StrContext::Expected(c) => Some(c),
791                    _ => None,
792                })
793                .collect::<alloc::vec::Vec<_>>();
794
795            let mut newline = false;
796
797            if let Some(expression) = expression {
798                newline = true;
799
800                write!(f, "invalid {expression}")?;
801            }
802
803            if !expected.is_empty() {
804                if newline {
805                    writeln!(f)?;
806                }
807                newline = true;
808
809                write!(f, "expected ")?;
810                for (i, expected) in expected.iter().enumerate() {
811                    if i != 0 {
812                        write!(f, ", ")?;
813                    }
814                    write!(f, "{expected}")?;
815                }
816            }
817            #[cfg(feature = "std")]
818            {
819                if let Some(cause) = self.cause() {
820                    if newline {
821                        writeln!(f)?;
822                    }
823                    write!(f, "{cause}")?;
824                }
825            }
826        }
827
828        Ok(())
829    }
830}
831
832impl<C> ErrorConvert<ContextError<C>> for ContextError<C> {
833    #[inline]
834    fn convert(self) -> ContextError<C> {
835        self
836    }
837}
838
839/// Additional parse context for [`ContextError`] added via [`Parser::context`]
840#[derive(Clone, Debug, PartialEq, Eq)]
841#[non_exhaustive]
842pub enum StrContext {
843    /// Description of what is currently being parsed
844    Label(&'static str),
845    /// Grammar item that was expected
846    Expected(StrContextValue),
847}
848
849impl core::fmt::Display for StrContext {
850    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
851        match self {
852            Self::Label(name) => write!(f, "invalid {name}"),
853            Self::Expected(value) => write!(f, "expected {value}"),
854        }
855    }
856}
857
858/// See [`StrContext`]
859#[derive(Clone, Debug, PartialEq, Eq)]
860#[non_exhaustive]
861pub enum StrContextValue {
862    /// A [`char`] token
863    CharLiteral(char),
864    /// A [`&str`] token
865    StringLiteral(&'static str),
866    /// A description of what was being parsed
867    Description(&'static str),
868}
869
870impl From<char> for StrContextValue {
871    #[inline]
872    fn from(inner: char) -> Self {
873        Self::CharLiteral(inner)
874    }
875}
876
877impl From<&'static str> for StrContextValue {
878    #[inline]
879    fn from(inner: &'static str) -> Self {
880        Self::StringLiteral(inner)
881    }
882}
883
884impl core::fmt::Display for StrContextValue {
885    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
886        match self {
887            Self::CharLiteral('\n') => "newline".fmt(f),
888            Self::CharLiteral('`') => "'`'".fmt(f),
889            Self::CharLiteral(c) if c.is_ascii_control() => {
890                write!(f, "`{}`", c.escape_debug())
891            }
892            Self::CharLiteral(c) => write!(f, "`{c}`"),
893            Self::StringLiteral(c) => write!(f, "`{c}`"),
894            Self::Description(c) => write!(f, "{c}"),
895        }
896    }
897}
898
899/// Trace all error paths, particularly for tests
900#[derive(Debug)]
901#[cfg(feature = "std")]
902pub enum TreeError<I, C = StrContext> {
903    /// Initial error that kicked things off
904    Base(TreeErrorBase<I>),
905    /// Traces added to the error while walking back up the stack
906    Stack {
907        /// Initial error that kicked things off
908        base: Box<Self>,
909        /// Traces added to the error while walking back up the stack
910        stack: Vec<TreeErrorFrame<I, C>>,
911    },
912    /// All failed branches of an `alt`
913    Alt(Vec<Self>),
914}
915
916/// See [`TreeError::Stack`]
917#[derive(Debug)]
918#[cfg(feature = "std")]
919pub enum TreeErrorFrame<I, C = StrContext> {
920    /// See [`ParserError::append`]
921    Kind(TreeErrorBase<I>),
922    /// See [`AddContext::add_context`]
923    Context(TreeErrorContext<I, C>),
924}
925
926/// See [`TreeErrorFrame::Kind`], [`ParserError::append`]
927#[derive(Debug)]
928#[cfg(feature = "std")]
929pub struct TreeErrorBase<I> {
930    /// Parsed input, at the location where the error occurred
931    pub input: I,
932    /// See [`FromExternalError::from_external_error`]
933    pub cause: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
934}
935
936/// See [`TreeErrorFrame::Context`], [`AddContext::add_context`]
937#[derive(Debug)]
938#[cfg(feature = "std")]
939pub struct TreeErrorContext<I, C = StrContext> {
940    /// Parsed input, at the location where the error occurred
941    pub input: I,
942    /// See [`AddContext::add_context`]
943    pub context: C,
944}
945
946#[cfg(feature = "std")]
947impl<I: ToOwned, C> TreeError<&I, C> {
948    /// Obtaining ownership
949    pub fn into_owned(self) -> TreeError<<I as ToOwned>::Owned, C> {
950        self.map_input(ToOwned::to_owned)
951    }
952}
953
954#[cfg(feature = "std")]
955impl<I, C> TreeError<I, C> {
956    /// Translate the input type
957    pub fn map_input<I2, O: Clone + Fn(I) -> I2>(self, op: O) -> TreeError<I2, C> {
958        match self {
959            TreeError::Base(base) => TreeError::Base(TreeErrorBase {
960                input: op(base.input),
961                cause: base.cause,
962            }),
963            TreeError::Stack { base, stack } => {
964                let base = Box::new(base.map_input(op.clone()));
965                let stack = stack
966                    .into_iter()
967                    .map(|frame| match frame {
968                        TreeErrorFrame::Kind(kind) => TreeErrorFrame::Kind(TreeErrorBase {
969                            input: op(kind.input),
970                            cause: kind.cause,
971                        }),
972                        TreeErrorFrame::Context(context) => {
973                            TreeErrorFrame::Context(TreeErrorContext {
974                                input: op(context.input),
975                                context: context.context,
976                            })
977                        }
978                    })
979                    .collect();
980                TreeError::Stack { base, stack }
981            }
982            TreeError::Alt(alt) => {
983                TreeError::Alt(alt.into_iter().map(|e| e.map_input(op.clone())).collect())
984            }
985        }
986    }
987
988    fn append_frame(self, frame: TreeErrorFrame<I, C>) -> Self {
989        match self {
990            TreeError::Stack { base, mut stack } => {
991                stack.push(frame);
992                TreeError::Stack { base, stack }
993            }
994            base => TreeError::Stack {
995                base: Box::new(base),
996                stack: vec![frame],
997            },
998        }
999    }
1000}
1001
1002#[cfg(feature = "std")]
1003impl<I, C> ParserError<I> for TreeError<I, C>
1004where
1005    I: Stream + Clone,
1006{
1007    type Inner = Self;
1008
1009    fn from_input(input: &I) -> Self {
1010        TreeError::Base(TreeErrorBase {
1011            input: input.clone(),
1012            cause: None,
1013        })
1014    }
1015
1016    fn append(self, input: &I, token_start: &<I as Stream>::Checkpoint) -> Self {
1017        let mut input = input.clone();
1018        input.reset(token_start);
1019        let frame = TreeErrorFrame::Kind(TreeErrorBase { input, cause: None });
1020        self.append_frame(frame)
1021    }
1022
1023    fn or(self, other: Self) -> Self {
1024        match (self, other) {
1025            (TreeError::Alt(mut first), TreeError::Alt(second)) => {
1026                // Just in case an implementation does a divide-and-conquer algorithm
1027                //
1028                // To prevent mixing `alt`s at different levels, parsers should
1029                // `alt_err.append(input)`.
1030                first.extend(second);
1031                TreeError::Alt(first)
1032            }
1033            (TreeError::Alt(mut alt), new) | (new, TreeError::Alt(mut alt)) => {
1034                alt.push(new);
1035                TreeError::Alt(alt)
1036            }
1037            (first, second) => TreeError::Alt(vec![first, second]),
1038        }
1039    }
1040
1041    #[inline(always)]
1042    fn into_inner(self) -> Result<Self::Inner, Self> {
1043        Ok(self)
1044    }
1045}
1046
1047#[cfg(feature = "std")]
1048impl<I, C> AddContext<I, C> for TreeError<I, C>
1049where
1050    I: Stream + Clone,
1051{
1052    fn add_context(self, input: &I, token_start: &<I as Stream>::Checkpoint, context: C) -> Self {
1053        let mut input = input.clone();
1054        input.reset(token_start);
1055        let frame = TreeErrorFrame::Context(TreeErrorContext { input, context });
1056        self.append_frame(frame)
1057    }
1058}
1059
1060#[cfg(feature = "std")]
1061#[cfg(feature = "unstable-recover")]
1062impl<I: Stream, C> FromRecoverableError<I, Self> for TreeError<I, C> {
1063    #[inline]
1064    fn from_recoverable_error(
1065        _token_start: &<I as Stream>::Checkpoint,
1066        _err_start: &<I as Stream>::Checkpoint,
1067        _input: &I,
1068        e: Self,
1069    ) -> Self {
1070        e
1071    }
1072}
1073
1074#[cfg(feature = "std")]
1075impl<I, C, E: std::error::Error + Send + Sync + 'static> FromExternalError<I, E> for TreeError<I, C>
1076where
1077    I: Clone,
1078{
1079    fn from_external_error(input: &I, e: E) -> Self {
1080        TreeError::Base(TreeErrorBase {
1081            input: input.clone(),
1082            cause: Some(Box::new(e)),
1083        })
1084    }
1085}
1086
1087#[cfg(feature = "std")]
1088impl<I, C> ErrorConvert<TreeError<(I, usize), C>> for TreeError<I, C> {
1089    #[inline]
1090    fn convert(self) -> TreeError<(I, usize), C> {
1091        self.map_input(|i| (i, 0))
1092    }
1093}
1094
1095#[cfg(feature = "std")]
1096impl<I, C> ErrorConvert<TreeError<I, C>> for TreeError<(I, usize), C> {
1097    #[inline]
1098    fn convert(self) -> TreeError<I, C> {
1099        self.map_input(|(i, _o)| i)
1100    }
1101}
1102
1103#[cfg(feature = "std")]
1104impl<I, C> TreeError<I, C>
1105where
1106    I: core::fmt::Display,
1107    C: fmt::Display,
1108{
1109    fn write(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
1110        let child_indent = indent + 2;
1111        match self {
1112            TreeError::Base(base) => {
1113                writeln!(f, "{:indent$}{base}", "")?;
1114            }
1115            TreeError::Stack { base, stack } => {
1116                base.write(f, indent)?;
1117                for (level, frame) in stack.iter().enumerate() {
1118                    match frame {
1119                        TreeErrorFrame::Kind(frame) => {
1120                            writeln!(f, "{:child_indent$}{level}: {frame}", "")?;
1121                        }
1122                        TreeErrorFrame::Context(frame) => {
1123                            writeln!(f, "{:child_indent$}{level}: {frame}", "")?;
1124                        }
1125                    }
1126                }
1127            }
1128            TreeError::Alt(alt) => {
1129                writeln!(f, "{:indent$}during one of:", "")?;
1130                for child in alt {
1131                    child.write(f, child_indent)?;
1132                }
1133            }
1134        }
1135
1136        Ok(())
1137    }
1138}
1139
1140#[cfg(feature = "std")]
1141impl<I: fmt::Display> fmt::Display for TreeErrorBase<I> {
1142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1143        if let Some(cause) = self.cause.as_ref() {
1144            write!(f, "caused by {cause}")?;
1145        }
1146        let input = abbreviate(self.input.to_string());
1147        write!(f, " at '{input}'")?;
1148        Ok(())
1149    }
1150}
1151
1152#[cfg(feature = "std")]
1153impl<I: fmt::Display, C: fmt::Display> fmt::Display for TreeErrorContext<I, C> {
1154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1155        let context = &self.context;
1156        let input = abbreviate(self.input.to_string());
1157        write!(f, "{context} at '{input}'")?;
1158        Ok(())
1159    }
1160}
1161
1162#[cfg(feature = "std")]
1163impl<I: fmt::Debug + fmt::Display + Sync + Send + 'static, C: fmt::Display + fmt::Debug>
1164    std::error::Error for TreeError<I, C>
1165{
1166}
1167
1168#[cfg(feature = "std")]
1169fn abbreviate(input: String) -> String {
1170    let mut abbrev = None;
1171
1172    if let Some((line, _)) = input.split_once('\n') {
1173        abbrev = Some(line);
1174    }
1175
1176    let max_len = 20;
1177    let current = abbrev.unwrap_or(&input);
1178    if max_len < current.len() {
1179        if let Some((index, _)) = current.char_indices().nth(max_len) {
1180            abbrev = Some(&current[..index]);
1181        }
1182    }
1183
1184    if let Some(abbrev) = abbrev {
1185        format!("{abbrev}...")
1186    } else {
1187        input
1188    }
1189}
1190
1191#[cfg(feature = "std")]
1192impl<I: fmt::Display, C: fmt::Display> fmt::Display for TreeError<I, C> {
1193    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1194        self.write(f, 0)
1195    }
1196}
1197
1198/// See [`Parser::parse`]
1199#[derive(Clone, Debug, PartialEq, Eq)]
1200pub struct ParseError<I, E> {
1201    input: I,
1202    offset: usize,
1203    inner: E,
1204}
1205
1206impl<I: Stream, E: ParserError<I>> ParseError<I, E> {
1207    pub(crate) fn new(mut input: I, start: I::Checkpoint, inner: E) -> Self {
1208        let offset = input.offset_from(&start);
1209        input.reset(&start);
1210        Self {
1211            input,
1212            offset,
1213            inner,
1214        }
1215    }
1216}
1217
1218impl<I, E> ParseError<I, E> {
1219    /// The [`Stream`] at the initial location when parsing started
1220    #[inline]
1221    pub fn input(&self) -> &I {
1222        &self.input
1223    }
1224
1225    /// The location in [`ParseError::input`] where parsing failed
1226    ///
1227    /// To get the span for the `char` this points to, see [`ParseError::char_span`].
1228    ///
1229    /// <div class="warning">
1230    ///
1231    /// **Note:** This is an offset, not an index, and may point to the end of input
1232    /// (`input.len()`) on eof errors.
1233    ///
1234    /// </div>
1235    #[inline]
1236    pub fn offset(&self) -> usize {
1237        self.offset
1238    }
1239
1240    /// The original [`ParserError`]
1241    #[inline]
1242    pub fn inner(&self) -> &E {
1243        &self.inner
1244    }
1245
1246    /// The original [`ParserError`]
1247    #[inline]
1248    pub fn into_inner(self) -> E {
1249        self.inner
1250    }
1251}
1252
1253impl<I: AsBStr, E> ParseError<I, E> {
1254    /// The byte indices for the `char` at [`ParseError::offset`]
1255    #[inline]
1256    pub fn char_span(&self) -> core::ops::Range<usize> {
1257        char_boundary(self.input.as_bstr(), self.offset())
1258    }
1259}
1260
1261fn char_boundary(input: &[u8], offset: usize) -> core::ops::Range<usize> {
1262    let len = input.len();
1263    if offset == len {
1264        return offset..offset;
1265    }
1266
1267    let start = (0..(offset + 1).min(len))
1268        .rev()
1269        .find(|i| {
1270            input
1271                .get(*i)
1272                .copied()
1273                .map(is_utf8_char_boundary)
1274                .unwrap_or(false)
1275        })
1276        .unwrap_or(0);
1277    let end = (offset + 1..len)
1278        .find(|i| {
1279            input
1280                .get(*i)
1281                .copied()
1282                .map(is_utf8_char_boundary)
1283                .unwrap_or(false)
1284        })
1285        .unwrap_or(len);
1286    start..end
1287}
1288
1289/// Taken from `core::num`
1290const fn is_utf8_char_boundary(b: u8) -> bool {
1291    // This is bit magic equivalent to: b < 128 || b >= 192
1292    (b as i8) >= -0x40
1293}
1294
1295impl<I, E> core::fmt::Display for ParseError<I, E>
1296where
1297    I: AsBStr,
1298    E: core::fmt::Display,
1299{
1300    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1301        let input = self.input.as_bstr();
1302        let span_start = self.offset;
1303        let span_end = span_start;
1304        #[cfg(feature = "std")]
1305        if input.contains(&b'\n') {
1306            let (line_idx, col_idx) = translate_position(input, span_start);
1307            let line_num = line_idx + 1;
1308            let col_num = col_idx + 1;
1309            let gutter = line_num.to_string().len();
1310            let content = input
1311                .split(|c| *c == b'\n')
1312                .nth(line_idx)
1313                .expect("valid line number");
1314
1315            writeln!(f, "parse error at line {line_num}, column {col_num}")?;
1316            //   |
1317            for _ in 0..gutter {
1318                write!(f, " ")?;
1319            }
1320            writeln!(f, " |")?;
1321
1322            // 1 | 00:32:00.a999999
1323            write!(f, "{line_num} | ")?;
1324            writeln!(f, "{}", String::from_utf8_lossy(content))?;
1325
1326            //   |          ^
1327            for _ in 0..gutter {
1328                write!(f, " ")?;
1329            }
1330            write!(f, " | ")?;
1331            for _ in 0..col_idx {
1332                write!(f, " ")?;
1333            }
1334            // The span will be empty at eof, so we need to make sure we always print at least
1335            // one `^`
1336            write!(f, "^")?;
1337            for _ in (span_start + 1)..(span_end.min(span_start + content.len())) {
1338                write!(f, "^")?;
1339            }
1340            writeln!(f)?;
1341        } else {
1342            let content = input;
1343            writeln!(f, "{}", String::from_utf8_lossy(content))?;
1344            for _ in 0..span_start {
1345                write!(f, " ")?;
1346            }
1347            // The span will be empty at eof, so we need to make sure we always print at least
1348            // one `^`
1349            write!(f, "^")?;
1350            for _ in (span_start + 1)..(span_end.min(span_start + content.len())) {
1351                write!(f, "^")?;
1352            }
1353            writeln!(f)?;
1354        }
1355        write!(f, "{}", self.inner)?;
1356
1357        Ok(())
1358    }
1359}
1360
1361#[cfg(feature = "std")]
1362fn translate_position(input: &[u8], index: usize) -> (usize, usize) {
1363    if input.is_empty() {
1364        return (0, index);
1365    }
1366
1367    let safe_index = index.min(input.len() - 1);
1368    let column_offset = index - safe_index;
1369    let index = safe_index;
1370
1371    let nl = input[0..index]
1372        .iter()
1373        .rev()
1374        .enumerate()
1375        .find(|(_, b)| **b == b'\n')
1376        .map(|(nl, _)| index - nl - 1);
1377    let line_start = match nl {
1378        Some(nl) => nl + 1,
1379        None => 0,
1380    };
1381    let line = input[0..line_start].iter().filter(|b| **b == b'\n').count();
1382
1383    // HACK: This treats byte offset and column offsets the same
1384    let column = core::str::from_utf8(&input[line_start..=index])
1385        .map(|s| s.chars().count() - 1)
1386        .unwrap_or_else(|_| index - line_start);
1387    let column = column + column_offset;
1388
1389    (line, column)
1390}
1391
1392#[cfg(test)]
1393mod test_char_boundary {
1394    use super::*;
1395
1396    #[test]
1397    fn ascii() {
1398        let input = "hi";
1399        let cases = [(0, 0..1), (1, 1..2), (2, 2..2)];
1400        for (offset, expected) in cases {
1401            assert_eq!(
1402                char_boundary(input.as_bytes(), offset),
1403                expected,
1404                "input={input:?}, offset={offset:?}"
1405            );
1406        }
1407    }
1408
1409    #[test]
1410    fn utf8() {
1411        let input = "βèƒôřè";
1412        assert_eq!(input.len(), 12);
1413        let cases = [
1414            (0, 0..2),
1415            (1, 0..2),
1416            (2, 2..4),
1417            (3, 2..4),
1418            (4, 4..6),
1419            (5, 4..6),
1420            (6, 6..8),
1421            (7, 6..8),
1422            (8, 8..10),
1423            (9, 8..10),
1424            (10, 10..12),
1425            (11, 10..12),
1426            (12, 12..12),
1427        ];
1428        for (offset, expected) in cases {
1429            assert_eq!(
1430                char_boundary(input.as_bytes(), offset),
1431                expected,
1432                "input={input:?}, offset={offset:?}"
1433            );
1434        }
1435    }
1436}
1437
1438#[cfg(test)]
1439#[cfg(feature = "std")]
1440mod test_parse_error {
1441    use super::*;
1442
1443    #[test]
1444    fn single_line() {
1445        let mut input = "0xZ123";
1446        let start = input.checkpoint();
1447        let _ = input.next_token().unwrap();
1448        let _ = input.next_token().unwrap();
1449        let inner = InputError::at(input);
1450        let error = ParseError::new(input, start, inner);
1451        let expected = "\
14520xZ123
1453  ^
1454failed to parse starting at: Z123";
1455        assert_eq!(error.to_string(), expected);
1456    }
1457}
1458
1459#[cfg(test)]
1460#[cfg(feature = "std")]
1461mod test_translate_position {
1462    use super::*;
1463
1464    #[test]
1465    fn empty() {
1466        let input = b"";
1467        let index = 0;
1468        let position = translate_position(&input[..], index);
1469        assert_eq!(position, (0, 0));
1470    }
1471
1472    #[test]
1473    fn start() {
1474        let input = b"Hello";
1475        let index = 0;
1476        let position = translate_position(&input[..], index);
1477        assert_eq!(position, (0, 0));
1478    }
1479
1480    #[test]
1481    fn end() {
1482        let input = b"Hello";
1483        let index = input.len() - 1;
1484        let position = translate_position(&input[..], index);
1485        assert_eq!(position, (0, input.len() - 1));
1486    }
1487
1488    #[test]
1489    fn after() {
1490        let input = b"Hello";
1491        let index = input.len();
1492        let position = translate_position(&input[..], index);
1493        assert_eq!(position, (0, input.len()));
1494    }
1495
1496    #[test]
1497    fn first_line() {
1498        let input = b"Hello\nWorld\n";
1499        let index = 2;
1500        let position = translate_position(&input[..], index);
1501        assert_eq!(position, (0, 2));
1502    }
1503
1504    #[test]
1505    fn end_of_line() {
1506        let input = b"Hello\nWorld\n";
1507        let index = 5;
1508        let position = translate_position(&input[..], index);
1509        assert_eq!(position, (0, 5));
1510    }
1511
1512    #[test]
1513    fn start_of_second_line() {
1514        let input = b"Hello\nWorld\n";
1515        let index = 6;
1516        let position = translate_position(&input[..], index);
1517        assert_eq!(position, (1, 0));
1518    }
1519
1520    #[test]
1521    fn second_line() {
1522        let input = b"Hello\nWorld\n";
1523        let index = 8;
1524        let position = translate_position(&input[..], index);
1525        assert_eq!(position, (1, 2));
1526    }
1527}