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