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