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: NonZero) => 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: NonZero) => 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: E| 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: Needed) => ErrMode::Incomplete(n),
238 ErrMode::Cut(InputError { input: T, kind: ErrorKind }) => ErrMode::Cut(InputError {
239 input: f(input),
240 kind,
241 }),
242 ErrMode::Backtrack(InputError { input: T, kind: ErrorKind }) => 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: &NonZero)) => write!(f, "Parsing requires {} bytes/chars", u),
259 ErrMode::Incomplete(Needed::Unknown) => write!(f, "Parsing requires more data"),
260 ErrMode::Cut(c: &E) => write!(f, "Parsing Failure: {:?}", c),
261 ErrMode::Backtrack(c: &E) => 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(op: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: &Box| 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: ContextError = 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: &&str) => write!(f, "invalid {name}"),
698 Self::Expected(value: &StrContextValue) => 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: &char) if c.is_ascii_control() => {
735 write!(f, "`{}`", c.escape_debug())
736 }
737 Self::CharLiteral(c: &char) => write!(f, "`{}`", c),
738 Self::StringLiteral(c: &&str) => write!(f, "`{}`", c),
739 Self::Description(c: &&str) => 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(op: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 = 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: &Box) = self.cause.as_ref() {
980 write!(f, "caused by {cause}")?;
981 } else {
982 let kind: &str = self.kind.description();
983 write!(f, "in {kind}")?;
984 }
985 let input: String = 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: &C = &self.context;
995 let input: String = 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: Option<&str> = None;
1012
1013 if let Some((line: &str, _)) = input.split_once(delimiter:'\n') {
1014 abbrev = Some(line);
1015 }
1016
1017 let max_len: usize = 20;
1018 let current: &str = abbrev.unwrap_or(&input);
1019 if max_len < current.len() {
1020 if let Some((index: usize, _)) = current.char_indices().nth(max_len) {
1021 abbrev = Some(&current[..index]);
1022 }
1023 }
1024
1025 if let Some(abbrev: &str) = 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, indent: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(op: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: I, k: VerboseErrorKind)| (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: &I, error: &VerboseErrorKind) in &self.errors {
1127 match error {
1128 VerboseErrorKind::Winnow(e: &ErrorKind) => writeln!(f, "{} at: {}", e.description(), input)?,
1129 VerboseErrorKind::Context(s: &C) => 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: usize = input.offset_from(&start);
1227 input.reset(checkpoint: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);
1468