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)]
63#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
64pub enum Needed {
65    /// Needs more data, but we do not know how much
66    Unknown,
67    /// Contains the required data size in bytes
68    Size(NonZeroUsize),
69}
70
71impl Needed {
72    /// Creates `Needed` instance, returns `Needed::Unknown` if the argument is zero
73    pub fn new(s: usize) -> Self {
74        match NonZeroUsize::new(s) {
75            Some(sz) => Needed::Size(sz),
76            None => Needed::Unknown,
77        }
78    }
79
80    /// Indicates if we know how many bytes we need
81    pub fn is_known(&self) -> bool {
82        *self != Needed::Unknown
83    }
84
85    /// Maps a `Needed` to `Needed` by applying a function to a contained `Size` value.
86    #[inline]
87    pub fn map<F: Fn(NonZeroUsize) -> usize>(self, f: F) -> Needed {
88        match self {
89            Needed::Unknown => Needed::Unknown,
90            Needed::Size(n) => Needed::new(f(n)),
91        }
92    }
93}
94
95/// Add parse error state to [`ParserError`]s
96#[derive(Debug, Clone, PartialEq)]
97#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
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, 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::Backtrack(E::assert(input, message))
194    }
195
196    #[inline]
197    fn append(self, input: &I, kind: ErrorKind) -> Self {
198        match self {
199            ErrMode::Backtrack(e) => ErrMode::Backtrack(e.append(input, 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, C, E: AddContext<I, C>> AddContext<I, C> for ErrMode<E> {
224    #[inline(always)]
225    fn add_context(self, input: &I, ctx: C) -> Self {
226        self.map(|err| err.add_context(input, ctx))
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 {} bytes/chars", u),
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>: 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 `{}` failed at {:#?}", _message, 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, 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, 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(self, _input: &I, _ctx: C) -> Self {
312        self
313    }
314}
315
316/// Capture context from when an error was recovered
317pub trait FromRecoverableError<I: Stream, E> {
318    /// Capture context from when an error was recovered
319    fn from_recoverable_error(
320        token_start: &<I as Stream>::Checkpoint,
321        err_start: &<I as Stream>::Checkpoint,
322        input: &I,
323        e: E,
324    ) -> Self;
325}
326
327/// Create a new error with an external error, from [`std::str::FromStr`]
328///
329/// This trait is required by the [`Parser::try_map`] combinator.
330pub trait FromExternalError<I, E> {
331    /// Like [`ParserError::from_error_kind`] but also include an external error.
332    fn from_external_error(input: &I, kind: ErrorKind, e: E) -> Self;
333}
334
335/// Equivalent of `From` implementation to avoid orphan rules in bits parsers
336pub trait ErrorConvert<E> {
337    /// Transform to another error type
338    fn convert(self) -> E;
339}
340
341/// Capture input on error
342///
343/// This is useful for testing of generic parsers to ensure the error happens at the right
344/// location.
345///
346/// **Note:** [context][Parser::context] and inner errors (like from [`Parser::try_map`]) will be
347/// dropped.
348#[derive(Copy, Clone, Debug, Eq, PartialEq)]
349pub struct InputError<I: Clone> {
350    /// The input stream, pointing to the location where the error occurred
351    pub input: I,
352    /// A rudimentary error kind
353    pub kind: ErrorKind,
354}
355
356impl<I: Clone> InputError<I> {
357    /// Creates a new basic error
358    #[inline]
359    pub fn new(input: I, kind: ErrorKind) -> Self {
360        Self { input, kind }
361    }
362
363    /// Translate the input type
364    #[inline]
365    pub fn map_input<I2: Clone, O: Fn(I) -> I2>(self, op: O) -> InputError<I2> {
366        InputError {
367            input: op(self.input),
368            kind: self.kind,
369        }
370    }
371}
372
373#[cfg(feature = "alloc")]
374impl<'i, I: ToOwned> InputError<&'i I>
375where
376    <I as ToOwned>::Owned: Clone,
377{
378    /// Obtaining ownership
379    pub fn into_owned(self) -> InputError<<I as ToOwned>::Owned> {
380        self.map_input(ToOwned::to_owned)
381    }
382}
383
384impl<I: Clone> ParserError<I> for InputError<I> {
385    #[inline]
386    fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
387        Self {
388            input: input.clone(),
389            kind,
390        }
391    }
392
393    #[inline]
394    fn append(self, _: &I, _: ErrorKind) -> Self {
395        self
396    }
397}
398
399impl<I: Clone, C> AddContext<I, C> for InputError<I> {}
400
401impl<I: Clone + Stream> FromRecoverableError<I, Self> for InputError<I> {
402    #[inline]
403    fn from_recoverable_error(
404        _token_start: &<I as Stream>::Checkpoint,
405        _err_start: &<I as Stream>::Checkpoint,
406        _input: &I,
407        e: Self,
408    ) -> Self {
409        e
410    }
411}
412
413impl<I: Clone, E> FromExternalError<I, E> for InputError<I> {
414    /// Create a new error from an input position and an external error
415    #[inline]
416    fn from_external_error(input: &I, kind: ErrorKind, _e: E) -> Self {
417        Self {
418            input: input.clone(),
419            kind,
420        }
421    }
422}
423
424impl<I: Clone> ErrorConvert<InputError<(I, usize)>> for InputError<I> {
425    #[inline]
426    fn convert(self) -> InputError<(I, usize)> {
427        InputError {
428            input: (self.input, 0),
429            kind: self.kind,
430        }
431    }
432}
433
434impl<I: Clone> ErrorConvert<InputError<I>> for InputError<(I, usize)> {
435    #[inline]
436    fn convert(self) -> InputError<I> {
437        InputError {
438            input: self.input.0,
439            kind: self.kind,
440        }
441    }
442}
443
444/// The Display implementation allows the `std::error::Error` implementation
445impl<I: Clone + fmt::Display> fmt::Display for InputError<I> {
446    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447        write!(
448            f,
449            "{} error starting at: {}",
450            self.kind.description(),
451            self.input
452        )
453    }
454}
455
456#[cfg(feature = "std")]
457impl<I: Clone + fmt::Debug + fmt::Display + Sync + Send + 'static> std::error::Error
458    for InputError<I>
459{
460}
461
462impl<I> ParserError<I> for () {
463    #[inline]
464    fn from_error_kind(_: &I, _: ErrorKind) -> Self {}
465
466    #[inline]
467    fn append(self, _: &I, _: ErrorKind) -> Self {}
468}
469
470impl<I, C> AddContext<I, C> for () {}
471
472impl<I: Stream> FromRecoverableError<I, Self> for () {
473    #[inline]
474    fn from_recoverable_error(
475        _token_start: &<I as Stream>::Checkpoint,
476        _err_start: &<I as Stream>::Checkpoint,
477        _input: &I,
478        (): Self,
479    ) -> Self {
480    }
481}
482
483impl<I, E> FromExternalError<I, E> for () {
484    #[inline]
485    fn from_external_error(_input: &I, _kind: ErrorKind, _e: E) -> Self {}
486}
487
488impl ErrorConvert<()> for () {
489    #[inline]
490    fn convert(self) {}
491}
492
493/// Accumulate context while backtracking errors
494#[derive(Debug)]
495pub struct ContextError<C = StrContext> {
496    #[cfg(feature = "alloc")]
497    context: crate::lib::std::vec::Vec<C>,
498    #[cfg(not(feature = "alloc"))]
499    context: core::marker::PhantomData<C>,
500    #[cfg(feature = "std")]
501    cause: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
502}
503
504impl<C> ContextError<C> {
505    /// Create an empty error
506    #[inline]
507    pub fn new() -> Self {
508        Self {
509            context: Default::default(),
510            #[cfg(feature = "std")]
511            cause: None,
512        }
513    }
514
515    /// Access context from [`Parser::context`]
516    #[inline]
517    #[cfg(feature = "alloc")]
518    pub fn context(&self) -> impl Iterator<Item = &C> {
519        self.context.iter()
520    }
521
522    /// Originating [`std::error::Error`]
523    #[inline]
524    #[cfg(feature = "std")]
525    pub fn cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
526        self.cause.as_deref()
527    }
528}
529
530impl<C: Clone> Clone for ContextError<C> {
531    fn clone(&self) -> Self {
532        Self {
533            context: self.context.clone(),
534            #[cfg(feature = "std")]
535            cause: self.cause.as_ref().map(|e| e.to_string().into()),
536        }
537    }
538}
539
540impl<C> Default for ContextError<C> {
541    #[inline]
542    fn default() -> Self {
543        Self::new()
544    }
545}
546
547impl<I, C> ParserError<I> for ContextError<C> {
548    #[inline]
549    fn from_error_kind(_input: &I, _kind: ErrorKind) -> Self {
550        Self::new()
551    }
552
553    #[inline]
554    fn append(self, _input: &I, _kind: ErrorKind) -> Self {
555        self
556    }
557
558    #[inline]
559    fn or(self, other: Self) -> Self {
560        other
561    }
562}
563
564impl<C, I> AddContext<I, C> for ContextError<C> {
565    #[inline]
566    fn add_context(mut self, _input: &I, ctx: C) -> Self {
567        #[cfg(feature = "alloc")]
568        self.context.push(ctx);
569        self
570    }
571}
572
573impl<I: Stream, C> FromRecoverableError<I, Self> for ContextError<C> {
574    #[inline]
575    fn from_recoverable_error(
576        _token_start: &<I as Stream>::Checkpoint,
577        _err_start: &<I as Stream>::Checkpoint,
578        _input: &I,
579        e: Self,
580    ) -> Self {
581        e
582    }
583}
584
585#[cfg(feature = "std")]
586impl<C, I, E: std::error::Error + Send + Sync + 'static> FromExternalError<I, E>
587    for ContextError<C>
588{
589    #[inline]
590    fn from_external_error(_input: &I, _kind: ErrorKind, e: E) -> Self {
591        let mut err = Self::new();
592        {
593            err.cause = Some(Box::new(e));
594        }
595        err
596    }
597}
598
599// HACK: This is more general than `std`, making the features non-additive
600#[cfg(not(feature = "std"))]
601impl<C, I, E: Send + Sync + 'static> FromExternalError<I, E> for ContextError<C> {
602    #[inline]
603    fn from_external_error(_input: &I, _kind: ErrorKind, _e: E) -> Self {
604        let err = Self::new();
605        err
606    }
607}
608
609// For tests
610impl<C: core::cmp::PartialEq> core::cmp::PartialEq for ContextError<C> {
611    fn eq(&self, other: &Self) -> bool {
612        #[cfg(feature = "alloc")]
613        {
614            if self.context != other.context {
615                return false;
616            }
617        }
618        #[cfg(feature = "std")]
619        {
620            if self.cause.as_ref().map(ToString::to_string)
621                != other.cause.as_ref().map(ToString::to_string)
622            {
623                return false;
624            }
625        }
626
627        true
628    }
629}
630
631impl crate::lib::std::fmt::Display for ContextError<StrContext> {
632    fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
633        #[cfg(feature = "alloc")]
634        {
635            let expression = self.context().find_map(|c| match c {
636                StrContext::Label(c) => Some(c),
637                _ => None,
638            });
639            let expected = self
640                .context()
641                .filter_map(|c| match c {
642                    StrContext::Expected(c) => Some(c),
643                    _ => None,
644                })
645                .collect::<crate::lib::std::vec::Vec<_>>();
646
647            let mut newline = false;
648
649            if let Some(expression) = expression {
650                newline = true;
651
652                write!(f, "invalid {}", expression)?;
653            }
654
655            if !expected.is_empty() {
656                if newline {
657                    writeln!(f)?;
658                }
659                newline = true;
660
661                write!(f, "expected ")?;
662                for (i, expected) in expected.iter().enumerate() {
663                    if i != 0 {
664                        write!(f, ", ")?;
665                    }
666                    write!(f, "{}", expected)?;
667                }
668            }
669            #[cfg(feature = "std")]
670            {
671                if let Some(cause) = self.cause() {
672                    if newline {
673                        writeln!(f)?;
674                    }
675                    write!(f, "{}", cause)?;
676                }
677            }
678        }
679
680        Ok(())
681    }
682}
683
684/// Additional parse context for [`ContextError`] added via [`Parser::context`]
685#[derive(Clone, Debug, PartialEq, Eq)]
686#[non_exhaustive]
687pub enum StrContext {
688    /// Description of what is currently being parsed
689    Label(&'static str),
690    /// Grammar item that was expected
691    Expected(StrContextValue),
692}
693
694impl crate::lib::std::fmt::Display for StrContext {
695    fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
696        match self {
697            Self::Label(name) => write!(f, "invalid {name}"),
698            Self::Expected(value) => write!(f, "expected {value}"),
699        }
700    }
701}
702
703/// See [`StrContext`]
704#[derive(Clone, Debug, PartialEq, Eq)]
705#[non_exhaustive]
706pub enum StrContextValue {
707    /// A [`char`] token
708    CharLiteral(char),
709    /// A [`&str`] token
710    StringLiteral(&'static str),
711    /// A description of what was being parsed
712    Description(&'static str),
713}
714
715impl From<char> for StrContextValue {
716    #[inline]
717    fn from(inner: char) -> Self {
718        Self::CharLiteral(inner)
719    }
720}
721
722impl From<&'static str> for StrContextValue {
723    #[inline]
724    fn from(inner: &'static str) -> Self {
725        Self::StringLiteral(inner)
726    }
727}
728
729impl crate::lib::std::fmt::Display for StrContextValue {
730    fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
731        match self {
732            Self::CharLiteral('\n') => "newline".fmt(f),
733            Self::CharLiteral('`') => "'`'".fmt(f),
734            Self::CharLiteral(c) if c.is_ascii_control() => {
735                write!(f, "`{}`", c.escape_debug())
736            }
737            Self::CharLiteral(c) => write!(f, "`{}`", c),
738            Self::StringLiteral(c) => write!(f, "`{}`", c),
739            Self::Description(c) => write!(f, "{}", c),
740        }
741    }
742}
743
744/// Trace all error paths, particularly for tests
745#[derive(Debug)]
746#[cfg(feature = "std")]
747pub enum TreeError<I, C = StrContext> {
748    /// Initial error that kicked things off
749    Base(TreeErrorBase<I>),
750    /// Traces added to the error while walking back up the stack
751    Stack {
752        /// Initial error that kicked things off
753        base: Box<Self>,
754        /// Traces added to the error while walking back up the stack
755        stack: Vec<TreeErrorFrame<I, C>>,
756    },
757    /// All failed branches of an `alt`
758    Alt(Vec<Self>),
759}
760
761/// See [`TreeError::Stack`]
762#[derive(Debug)]
763#[cfg(feature = "std")]
764pub enum TreeErrorFrame<I, C = StrContext> {
765    /// See [`ParserError::append`]
766    Kind(TreeErrorBase<I>),
767    /// See [`AddContext::add_context`]
768    Context(TreeErrorContext<I, C>),
769}
770
771/// See [`TreeErrorFrame::Kind`], [`ParserError::append`]
772#[derive(Debug)]
773#[cfg(feature = "std")]
774pub struct TreeErrorBase<I> {
775    /// Parsed input, at the location where the error occurred
776    pub input: I,
777    /// Debug context
778    pub kind: ErrorKind,
779    /// See [`FromExternalError::from_external_error`]
780    pub cause: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
781}
782
783/// See [`TreeErrorFrame::Context`], [`AddContext::add_context`]
784#[derive(Debug)]
785#[cfg(feature = "std")]
786pub struct TreeErrorContext<I, C = StrContext> {
787    /// Parsed input, at the location where the error occurred
788    pub input: I,
789    /// See [`AddContext::add_context`]
790    pub context: C,
791}
792
793#[cfg(feature = "std")]
794impl<'i, I: ToOwned, C> TreeError<&'i I, C>
795where
796    <I as ToOwned>::Owned: Clone,
797{
798    /// Obtaining ownership
799    pub fn into_owned(self) -> TreeError<<I as ToOwned>::Owned, C> {
800        self.map_input(ToOwned::to_owned)
801    }
802}
803
804#[cfg(feature = "std")]
805impl<I, C> TreeError<I, C>
806where
807    I: Clone,
808{
809    /// Translate the input type
810    pub fn map_input<I2: Clone, O: Clone + Fn(I) -> I2>(self, op: O) -> TreeError<I2, C> {
811        match self {
812            TreeError::Base(base) => TreeError::Base(TreeErrorBase {
813                input: op(base.input),
814                kind: base.kind,
815                cause: base.cause,
816            }),
817            TreeError::Stack { base, stack } => {
818                let base = Box::new(base.map_input(op.clone()));
819                let stack = stack
820                    .into_iter()
821                    .map(|frame| match frame {
822                        TreeErrorFrame::Kind(kind) => TreeErrorFrame::Kind(TreeErrorBase {
823                            input: op(kind.input),
824                            kind: kind.kind,
825                            cause: kind.cause,
826                        }),
827                        TreeErrorFrame::Context(context) => {
828                            TreeErrorFrame::Context(TreeErrorContext {
829                                input: op(context.input),
830                                context: context.context,
831                            })
832                        }
833                    })
834                    .collect();
835                TreeError::Stack { base, stack }
836            }
837            TreeError::Alt(alt) => {
838                TreeError::Alt(alt.into_iter().map(|e| e.map_input(op.clone())).collect())
839            }
840        }
841    }
842
843    fn append_frame(self, frame: TreeErrorFrame<I, C>) -> Self {
844        match self {
845            TreeError::Stack { base, mut stack } => {
846                stack.push(frame);
847                TreeError::Stack { base, stack }
848            }
849            base => TreeError::Stack {
850                base: Box::new(base),
851                stack: vec![frame],
852            },
853        }
854    }
855}
856
857#[cfg(feature = "std")]
858impl<I, C> ParserError<I> for TreeError<I, C>
859where
860    I: Clone,
861{
862    fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
863        TreeError::Base(TreeErrorBase {
864            input: input.clone(),
865            kind,
866            cause: None,
867        })
868    }
869
870    fn append(self, input: &I, kind: ErrorKind) -> Self {
871        let frame = TreeErrorFrame::Kind(TreeErrorBase {
872            input: input.clone(),
873            kind,
874            cause: None,
875        });
876        self.append_frame(frame)
877    }
878
879    fn or(self, other: Self) -> Self {
880        match (self, other) {
881            (TreeError::Alt(mut first), TreeError::Alt(second)) => {
882                // Just in case an implementation does a divide-and-conquer algorithm
883                //
884                // To prevent mixing `alt`s at different levels, parsers should
885                // `alt_err.append(input, ErrorKind::Alt)`.
886                first.extend(second);
887                TreeError::Alt(first)
888            }
889            (TreeError::Alt(mut alt), new) | (new, TreeError::Alt(mut alt)) => {
890                alt.push(new);
891                TreeError::Alt(alt)
892            }
893            (first, second) => TreeError::Alt(vec![first, second]),
894        }
895    }
896}
897
898#[cfg(feature = "std")]
899impl<I, C> AddContext<I, C> for TreeError<I, C>
900where
901    I: Clone,
902{
903    fn add_context(self, input: &I, context: C) -> Self {
904        let frame = TreeErrorFrame::Context(TreeErrorContext {
905            input: input.clone(),
906            context,
907        });
908        self.append_frame(frame)
909    }
910}
911
912#[cfg(feature = "std")]
913impl<I: Clone + Stream, C> FromRecoverableError<I, Self> for TreeError<I, C> {
914    #[inline]
915    fn from_recoverable_error(
916        _token_start: &<I as Stream>::Checkpoint,
917        _err_start: &<I as Stream>::Checkpoint,
918        _input: &I,
919        e: Self,
920    ) -> Self {
921        e
922    }
923}
924
925#[cfg(feature = "std")]
926impl<I, C, E: std::error::Error + Send + Sync + 'static> FromExternalError<I, E> for TreeError<I, C>
927where
928    I: Clone,
929{
930    fn from_external_error(input: &I, kind: ErrorKind, e: E) -> Self {
931        TreeError::Base(TreeErrorBase {
932            input: input.clone(),
933            kind,
934            cause: Some(Box::new(e)),
935        })
936    }
937}
938
939#[cfg(feature = "std")]
940impl<I, C> TreeError<I, C>
941where
942    I: Clone + crate::lib::std::fmt::Display,
943    C: fmt::Display,
944{
945    fn write(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
946        let child_indent = indent + 2;
947        match self {
948            TreeError::Base(base) => {
949                writeln!(f, "{:indent$}{base}", "")?;
950            }
951            TreeError::Stack { base, stack } => {
952                base.write(f, indent)?;
953                for (level, frame) in stack.iter().enumerate() {
954                    match frame {
955                        TreeErrorFrame::Kind(frame) => {
956                            writeln!(f, "{:child_indent$}{level}: {frame}", "")?;
957                        }
958                        TreeErrorFrame::Context(frame) => {
959                            writeln!(f, "{:child_indent$}{level}: {frame}", "")?;
960                        }
961                    }
962                }
963            }
964            TreeError::Alt(alt) => {
965                writeln!(f, "{:indent$}during one of:", "")?;
966                for child in alt {
967                    child.write(f, child_indent)?;
968                }
969            }
970        }
971
972        Ok(())
973    }
974}
975
976#[cfg(feature = "std")]
977impl<I: Clone + fmt::Display> fmt::Display for TreeErrorBase<I> {
978    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
979        if let Some(cause) = self.cause.as_ref() {
980            write!(f, "caused by {cause}")?;
981        } else {
982            let kind = self.kind.description();
983            write!(f, "in {kind}")?;
984        }
985        let input = abbreviate(self.input.to_string());
986        write!(f, " at '{input}'")?;
987        Ok(())
988    }
989}
990
991#[cfg(feature = "std")]
992impl<I: Clone + fmt::Display, C: fmt::Display> fmt::Display for TreeErrorContext<I, C> {
993    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
994        let context = &self.context;
995        let input = abbreviate(self.input.to_string());
996        write!(f, "{context} at '{input}'")?;
997        Ok(())
998    }
999}
1000
1001#[cfg(feature = "std")]
1002impl<
1003        I: Clone + fmt::Debug + fmt::Display + Sync + Send + 'static,
1004        C: fmt::Display + fmt::Debug,
1005    > std::error::Error for TreeError<I, C>
1006{
1007}
1008
1009#[cfg(feature = "std")]
1010fn abbreviate(input: String) -> String {
1011    let mut abbrev = None;
1012
1013    if let Some((line, _)) = input.split_once('\n') {
1014        abbrev = Some(line);
1015    }
1016
1017    let max_len = 20;
1018    let current = abbrev.unwrap_or(&input);
1019    if max_len < current.len() {
1020        if let Some((index, _)) = current.char_indices().nth(max_len) {
1021            abbrev = Some(&current[..index]);
1022        }
1023    }
1024
1025    if let Some(abbrev) = abbrev {
1026        format!("{abbrev}...")
1027    } else {
1028        input
1029    }
1030}
1031
1032#[cfg(feature = "std")]
1033impl<I: Clone + fmt::Display, C: fmt::Display> fmt::Display for TreeError<I, C> {
1034    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1035        self.write(f, 0)
1036    }
1037}
1038
1039/// Deprecated, replaced with [`ContextError`]
1040#[cfg(feature = "std")]
1041#[allow(deprecated)]
1042#[deprecated(since = "0.5.8", note = "Replaced with `ContextError`")]
1043#[derive(Clone, Debug, Eq, PartialEq)]
1044pub struct VerboseError<I: Clone, C = &'static str> {
1045    /// Accumulated error information
1046    pub errors: crate::lib::std::vec::Vec<(I, VerboseErrorKind<C>)>,
1047}
1048
1049#[cfg(feature = "std")]
1050#[allow(deprecated)]
1051impl<'i, I: ToOwned, C> VerboseError<&'i I, C>
1052where
1053    <I as ToOwned>::Owned: Clone,
1054{
1055    /// Obtaining ownership
1056    pub fn into_owned(self) -> VerboseError<<I as ToOwned>::Owned, C> {
1057        self.map_input(ToOwned::to_owned)
1058    }
1059}
1060
1061#[cfg(feature = "std")]
1062#[allow(deprecated)]
1063impl<I: Clone, C> VerboseError<I, C> {
1064    /// Translate the input type
1065    pub fn map_input<I2: Clone, O>(self, op: O) -> VerboseError<I2, C>
1066    where
1067        O: Fn(I) -> I2,
1068    {
1069        VerboseError {
1070            errors: self.errors.into_iter().map(|(i, k)| (op(i), k)).collect(),
1071        }
1072    }
1073}
1074
1075/// Deprecated, replaced with [`ContextError`]
1076#[cfg(feature = "std")]
1077#[deprecated(since = "0.5.8", note = "Replaced with `ContextError`")]
1078#[derive(Clone, Debug, Eq, PartialEq)]
1079pub enum VerboseErrorKind<C = &'static str> {
1080    /// Static string added by the `context` function
1081    Context(C),
1082    /// Error kind given by various parsers
1083    Winnow(ErrorKind),
1084}
1085
1086#[cfg(feature = "std")]
1087#[allow(deprecated)]
1088impl<I: Clone, C> ParserError<I> for VerboseError<I, C> {
1089    fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
1090        VerboseError {
1091            errors: vec![(input.clone(), VerboseErrorKind::Winnow(kind))],
1092        }
1093    }
1094
1095    fn append(mut self, input: &I, kind: ErrorKind) -> Self {
1096        self.errors
1097            .push((input.clone(), VerboseErrorKind::Winnow(kind)));
1098        self
1099    }
1100}
1101
1102#[cfg(feature = "std")]
1103#[allow(deprecated)]
1104impl<I: Clone, C> AddContext<I, C> for VerboseError<I, C> {
1105    fn add_context(mut self, input: &I, ctx: C) -> Self {
1106        self.errors
1107            .push((input.clone(), VerboseErrorKind::Context(ctx)));
1108        self
1109    }
1110}
1111
1112#[cfg(feature = "std")]
1113#[allow(deprecated)]
1114impl<I: Clone, C, E> FromExternalError<I, E> for VerboseError<I, C> {
1115    /// Create a new error from an input position and an external error
1116    fn from_external_error(input: &I, kind: ErrorKind, _e: E) -> Self {
1117        Self::from_error_kind(input, kind)
1118    }
1119}
1120
1121#[cfg(feature = "std")]
1122#[allow(deprecated)]
1123impl<I: Clone + fmt::Display, C: fmt::Display> fmt::Display for VerboseError<I, C> {
1124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1125        writeln!(f, "Parse error:")?;
1126        for (input, error) in &self.errors {
1127            match error {
1128                VerboseErrorKind::Winnow(e) => writeln!(f, "{} at: {}", e.description(), input)?,
1129                VerboseErrorKind::Context(s) => writeln!(f, "in section '{}', at: {}", s, input)?,
1130            }
1131        }
1132
1133        Ok(())
1134    }
1135}
1136
1137#[cfg(feature = "std")]
1138#[allow(deprecated)]
1139impl<
1140        I: Clone + fmt::Debug + fmt::Display + Sync + Send + 'static,
1141        C: fmt::Display + fmt::Debug,
1142    > std::error::Error for VerboseError<I, C>
1143{
1144}
1145
1146/// Provide some minor debug context for errors
1147#[rustfmt::skip]
1148#[derive(Debug,PartialEq,Eq,Hash,Clone,Copy)]
1149#[allow(missing_docs)]
1150pub enum ErrorKind {
1151  Assert,
1152  Token,
1153  Tag,
1154  Alt,
1155  Many,
1156  Eof,
1157  Slice,
1158  Complete,
1159  Not,
1160  Verify,
1161  Fail,
1162}
1163
1164impl ErrorKind {
1165    #[rustfmt::skip]
1166    /// Converts an `ErrorKind` to a text description
1167    pub fn description(&self) -> &str {
1168    match *self {
1169      ErrorKind::Assert                    => "assert",
1170      ErrorKind::Token                     => "token",
1171      ErrorKind::Tag                       => "tag",
1172      ErrorKind::Alt                       => "alternative",
1173      ErrorKind::Many                      => "many",
1174      ErrorKind::Eof                       => "end of file",
1175      ErrorKind::Slice                     => "slice",
1176      ErrorKind::Complete                  => "complete",
1177      ErrorKind::Not                       => "negation",
1178      ErrorKind::Verify                    => "predicate verification",
1179      ErrorKind::Fail                      => "fail",
1180    }
1181  }
1182}
1183
1184impl<I> ParserError<I> for ErrorKind {
1185    #[inline]
1186    fn from_error_kind(_input: &I, kind: ErrorKind) -> Self {
1187        kind
1188    }
1189
1190    #[inline]
1191    fn append(self, _: &I, _: ErrorKind) -> Self {
1192        self
1193    }
1194}
1195
1196impl<I, C> AddContext<I, C> for ErrorKind {}
1197
1198impl<I, E> FromExternalError<I, E> for ErrorKind {
1199    /// Create a new error from an input position and an external error
1200    #[inline]
1201    fn from_external_error(_input: &I, kind: ErrorKind, _e: E) -> Self {
1202        kind
1203    }
1204}
1205
1206/// The Display implementation allows the `std::error::Error` implementation
1207impl fmt::Display for ErrorKind {
1208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1209        write!(f, "error {:?}", self)
1210    }
1211}
1212
1213#[cfg(feature = "std")]
1214impl std::error::Error for ErrorKind {}
1215
1216/// See [`Parser::parse`]
1217#[derive(Clone, Debug, PartialEq, Eq)]
1218pub struct ParseError<I, E> {
1219    input: I,
1220    offset: usize,
1221    inner: E,
1222}
1223
1224impl<I: Stream, E: ParserError<I>> ParseError<I, E> {
1225    pub(crate) fn new(mut input: I, start: I::Checkpoint, inner: E) -> Self {
1226        let offset = input.offset_from(&start);
1227        input.reset(start);
1228        Self {
1229            input,
1230            offset,
1231            inner,
1232        }
1233    }
1234}
1235
1236impl<I, E> ParseError<I, E> {
1237    /// The [`Stream`] at the initial location when parsing started
1238    #[inline]
1239    pub fn input(&self) -> &I {
1240        &self.input
1241    }
1242
1243    /// The location in [`ParseError::input`] where parsing failed
1244    ///
1245    /// **Note:** This is an offset, not an index, and may point to the end of input
1246    /// (`input.len()`) on eof errors.
1247    #[inline]
1248    pub fn offset(&self) -> usize {
1249        self.offset
1250    }
1251
1252    /// The original [`ParserError`]
1253    #[inline]
1254    pub fn inner(&self) -> &E {
1255        &self.inner
1256    }
1257
1258    /// The original [`ParserError`]
1259    #[inline]
1260    pub fn into_inner(self) -> E {
1261        self.inner
1262    }
1263}
1264
1265impl<I, E> core::fmt::Display for ParseError<I, E>
1266where
1267    I: AsBStr,
1268    E: core::fmt::Display,
1269{
1270    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1271        let input = self.input.as_bstr();
1272        let span_start = self.offset;
1273        let span_end = span_start;
1274        #[cfg(feature = "std")]
1275        if input.contains(&b'\n') {
1276            let (line_idx, col_idx) = translate_position(input, span_start);
1277            let line_num = line_idx + 1;
1278            let col_num = col_idx + 1;
1279            let gutter = line_num.to_string().len();
1280            let content = input
1281                .split(|c| *c == b'\n')
1282                .nth(line_idx)
1283                .expect("valid line number");
1284
1285            writeln!(f, "parse error at line {}, column {}", line_num, col_num)?;
1286            //   |
1287            for _ in 0..gutter {
1288                write!(f, " ")?;
1289            }
1290            writeln!(f, " |")?;
1291
1292            // 1 | 00:32:00.a999999
1293            write!(f, "{} | ", line_num)?;
1294            writeln!(f, "{}", String::from_utf8_lossy(content))?;
1295
1296            //   |          ^
1297            for _ in 0..gutter {
1298                write!(f, " ")?;
1299            }
1300            write!(f, " | ")?;
1301            for _ in 0..col_idx {
1302                write!(f, " ")?;
1303            }
1304            // The span will be empty at eof, so we need to make sure we always print at least
1305            // one `^`
1306            write!(f, "^")?;
1307            for _ in (span_start + 1)..(span_end.min(span_start + content.len())) {
1308                write!(f, "^")?;
1309            }
1310            writeln!(f)?;
1311        } else {
1312            let content = input;
1313            writeln!(f, "{}", String::from_utf8_lossy(content))?;
1314            for _ in 0..span_start {
1315                write!(f, " ")?;
1316            }
1317            // The span will be empty at eof, so we need to make sure we always print at least
1318            // one `^`
1319            write!(f, "^")?;
1320            for _ in (span_start + 1)..(span_end.min(span_start + content.len())) {
1321                write!(f, "^")?;
1322            }
1323            writeln!(f)?;
1324        }
1325        write!(f, "{}", self.inner)?;
1326
1327        Ok(())
1328    }
1329}
1330
1331#[cfg(feature = "std")]
1332fn translate_position(input: &[u8], index: usize) -> (usize, usize) {
1333    if input.is_empty() {
1334        return (0, index);
1335    }
1336
1337    let safe_index = index.min(input.len() - 1);
1338    let column_offset = index - safe_index;
1339    let index = safe_index;
1340
1341    let nl = input[0..index]
1342        .iter()
1343        .rev()
1344        .enumerate()
1345        .find(|(_, b)| **b == b'\n')
1346        .map(|(nl, _)| index - nl - 1);
1347    let line_start = match nl {
1348        Some(nl) => nl + 1,
1349        None => 0,
1350    };
1351    let line = input[0..line_start].iter().filter(|b| **b == b'\n').count();
1352
1353    // HACK: This treats byte offset and column offsets the same
1354    let column = crate::lib::std::str::from_utf8(&input[line_start..=index])
1355        .map(|s| s.chars().count() - 1)
1356        .unwrap_or_else(|_| index - line_start);
1357    let column = column + column_offset;
1358
1359    (line, column)
1360}
1361
1362#[cfg(test)]
1363#[cfg(feature = "std")]
1364mod test_parse_error {
1365    use super::*;
1366
1367    #[test]
1368    fn single_line() {
1369        let mut input = "0xZ123";
1370        let start = input.checkpoint();
1371        let _ = input.next_token().unwrap();
1372        let _ = input.next_token().unwrap();
1373        let inner = InputError::new(input, ErrorKind::Slice);
1374        let error = ParseError::new(input, start, inner);
1375        let expected = "\
13760xZ123
1377  ^
1378slice error starting at: Z123";
1379        assert_eq!(error.to_string(), expected);
1380    }
1381}
1382
1383#[cfg(test)]
1384#[cfg(feature = "std")]
1385mod test_translate_position {
1386    use super::*;
1387
1388    #[test]
1389    fn empty() {
1390        let input = b"";
1391        let index = 0;
1392        let position = translate_position(&input[..], index);
1393        assert_eq!(position, (0, 0));
1394    }
1395
1396    #[test]
1397    fn start() {
1398        let input = b"Hello";
1399        let index = 0;
1400        let position = translate_position(&input[..], index);
1401        assert_eq!(position, (0, 0));
1402    }
1403
1404    #[test]
1405    fn end() {
1406        let input = b"Hello";
1407        let index = input.len() - 1;
1408        let position = translate_position(&input[..], index);
1409        assert_eq!(position, (0, input.len() - 1));
1410    }
1411
1412    #[test]
1413    fn after() {
1414        let input = b"Hello";
1415        let index = input.len();
1416        let position = translate_position(&input[..], index);
1417        assert_eq!(position, (0, input.len()));
1418    }
1419
1420    #[test]
1421    fn first_line() {
1422        let input = b"Hello\nWorld\n";
1423        let index = 2;
1424        let position = translate_position(&input[..], index);
1425        assert_eq!(position, (0, 2));
1426    }
1427
1428    #[test]
1429    fn end_of_line() {
1430        let input = b"Hello\nWorld\n";
1431        let index = 5;
1432        let position = translate_position(&input[..], index);
1433        assert_eq!(position, (0, 5));
1434    }
1435
1436    #[test]
1437    fn start_of_second_line() {
1438        let input = b"Hello\nWorld\n";
1439        let index = 6;
1440        let position = translate_position(&input[..], index);
1441        assert_eq!(position, (1, 0));
1442    }
1443
1444    #[test]
1445    fn second_line() {
1446        let input = b"Hello\nWorld\n";
1447        let index = 8;
1448        let position = translate_position(&input[..], index);
1449        assert_eq!(position, (1, 2));
1450    }
1451}
1452
1453/// Creates a parse error from a [`ErrorKind`]
1454/// and the position in the input
1455#[cfg(test)]
1456macro_rules! error_position(
1457  ($input:expr, $code:expr) => ({
1458    $crate::error::ParserError::from_error_kind($input, $code)
1459  });
1460);
1461
1462#[cfg(test)]
1463macro_rules! error_node_position(
1464  ($input:expr, $code:expr, $next:expr) => ({
1465    $crate::error::ParserError::append($next, $input, $code)
1466  });
1467);