Coverage Report

Created: 2026-10-28 07:08

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