Coverage Report

Created: 2025-11-16 06:37

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