winnow/
error.rs

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