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: NonZero) => 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: NonZero) => 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: E1| 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: E| 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: Needed) => ErrMode::Incomplete(n),
311 ErrMode::Cut(InputError { input: T }) => ErrMode::Cut(InputError { input: f(input) }),
312 ErrMode::Backtrack(InputError { input: T }) => {
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: &NonZero)) => write!(f, "Parsing requires {u} more data"),
328 ErrMode::Incomplete(Needed::Unknown) => write!(f, "Parsing requires more data"),
329 ErrMode::Cut(c: &E) => write!(f, "Parsing Failure: {c:?}"),
330 ErrMode::Backtrack(c: &E) => 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(op: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| (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: I, _o: usize)| 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 /// Access context from [`Parser::context`]
702 #[inline]
703 #[cfg(feature = "alloc")]
704 pub fn context(&self) -> impl Iterator<Item = &C> {
705 self.context.iter()
706 }
707
708 /// Originating [`std::error::Error`]
709 #[inline]
710 #[cfg(feature = "std")]
711 pub fn cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
712 self.cause.as_deref()
713 }
714}
715
716impl<C: Clone> Clone for ContextError<C> {
717 fn clone(&self) -> Self {
718 Self {
719 context: self.context.clone(),
720 #[cfg(feature = "std")]
721 cause: self.cause.as_ref().map(|e: &Box| e.to_string().into()),
722 }
723 }
724}
725
726impl<C> Default for ContextError<C> {
727 #[inline]
728 fn default() -> Self {
729 Self::new()
730 }
731}
732
733impl<I: Stream, C> ParserError<I> for ContextError<C> {
734 type Inner = Self;
735
736 #[inline]
737 fn from_input(_input: &I) -> Self {
738 Self::new()
739 }
740
741 #[inline(always)]
742 fn into_inner(self) -> Result<Self::Inner, Self> {
743 Ok(self)
744 }
745}
746
747impl<C, I: Stream> AddContext<I, C> for ContextError<C> {
748 #[inline]
749 fn add_context(
750 mut self,
751 _input: &I,
752 _token_start: &<I as Stream>::Checkpoint,
753 context: C,
754 ) -> Self {
755 #[cfg(feature = "alloc")]
756 self.context.push(context);
757 self
758 }
759}
760
761#[cfg(feature = "unstable-recover")]
762#[cfg(feature = "std")]
763impl<I: Stream, C> FromRecoverableError<I, Self> for ContextError<C> {
764 #[inline]
765 fn from_recoverable_error(
766 _token_start: &<I as Stream>::Checkpoint,
767 _err_start: &<I as Stream>::Checkpoint,
768 _input: &I,
769 e: Self,
770 ) -> Self {
771 e
772 }
773}
774
775#[cfg(feature = "std")]
776impl<C, I, E: std::error::Error + Send + Sync + 'static> FromExternalError<I, E>
777 for ContextError<C>
778{
779 #[inline]
780 fn from_external_error(_input: &I, e: E) -> Self {
781 let mut err: ContextError = Self::new();
782 {
783 err.cause = Some(Box::new(e));
784 }
785 err
786 }
787}
788
789// HACK: This is more general than `std`, making the features non-additive
790#[cfg(not(feature = "std"))]
791impl<C, I, E: Send + Sync + 'static> FromExternalError<I, E> for ContextError<C> {
792 #[inline]
793 fn from_external_error(_input: &I, _e: E) -> Self {
794 let err = Self::new();
795 err
796 }
797}
798
799// For tests
800impl<C: core::cmp::PartialEq> core::cmp::PartialEq for ContextError<C> {
801 fn eq(&self, other: &Self) -> bool {
802 #[cfg(feature = "alloc")]
803 {
804 if self.context != other.context {
805 return false;
806 }
807 }
808 #[cfg(feature = "std")]
809 {
810 if self.cause.as_ref().map(ToString::to_string)
811 != other.cause.as_ref().map(ToString::to_string)
812 {
813 return false;
814 }
815 }
816
817 true
818 }
819}
820
821impl crate::lib::std::fmt::Display for ContextError<StrContext> {
822 fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
823 #[cfg(feature = "alloc")]
824 {
825 let expression = self.context().find_map(|c| match c {
826 StrContext::Label(c) => Some(c),
827 _ => None,
828 });
829 let expected = self
830 .context()
831 .filter_map(|c| match c {
832 StrContext::Expected(c) => Some(c),
833 _ => None,
834 })
835 .collect::<crate::lib::std::vec::Vec<_>>();
836
837 let mut newline = false;
838
839 if let Some(expression) = expression {
840 newline = true;
841
842 write!(f, "invalid {expression}")?;
843 }
844
845 if !expected.is_empty() {
846 if newline {
847 writeln!(f)?;
848 }
849 newline = true;
850
851 write!(f, "expected ")?;
852 for (i, expected) in expected.iter().enumerate() {
853 if i != 0 {
854 write!(f, ", ")?;
855 }
856 write!(f, "{expected}")?;
857 }
858 }
859 #[cfg(feature = "std")]
860 {
861 if let Some(cause) = self.cause() {
862 if newline {
863 writeln!(f)?;
864 }
865 write!(f, "{cause}")?;
866 }
867 }
868 }
869
870 Ok(())
871 }
872}
873
874impl<C> ErrorConvert<ContextError<C>> for ContextError<C> {
875 #[inline]
876 fn convert(self) -> ContextError<C> {
877 self
878 }
879}
880
881/// Additional parse context for [`ContextError`] added via [`Parser::context`]
882#[derive(Clone, Debug, PartialEq, Eq)]
883#[non_exhaustive]
884pub enum StrContext {
885 /// Description of what is currently being parsed
886 Label(&'static str),
887 /// Grammar item that was expected
888 Expected(StrContextValue),
889}
890
891impl crate::lib::std::fmt::Display for StrContext {
892 fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
893 match self {
894 Self::Label(name: &&'static str) => write!(f, "invalid {name}"),
895 Self::Expected(value: &StrContextValue) => write!(f, "expected {value}"),
896 }
897 }
898}
899
900/// See [`StrContext`]
901#[derive(Clone, Debug, PartialEq, Eq)]
902#[non_exhaustive]
903pub enum StrContextValue {
904 /// A [`char`] token
905 CharLiteral(char),
906 /// A [`&str`] token
907 StringLiteral(&'static str),
908 /// A description of what was being parsed
909 Description(&'static str),
910}
911
912impl From<char> for StrContextValue {
913 #[inline]
914 fn from(inner: char) -> Self {
915 Self::CharLiteral(inner)
916 }
917}
918
919impl From<&'static str> for StrContextValue {
920 #[inline]
921 fn from(inner: &'static str) -> Self {
922 Self::StringLiteral(inner)
923 }
924}
925
926impl crate::lib::std::fmt::Display for StrContextValue {
927 fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
928 match self {
929 Self::CharLiteral('\n') => "newline".fmt(f),
930 Self::CharLiteral('`') => "'`'".fmt(f),
931 Self::CharLiteral(c: &char) if c.is_ascii_control() => {
932 write!(f, "`{}`", c.escape_debug())
933 }
934 Self::CharLiteral(c: &char) => write!(f, "`{c}`"),
935 Self::StringLiteral(c: &&'static str) => write!(f, "`{c}`"),
936 Self::Description(c: &&'static str) => write!(f, "{c}"),
937 }
938 }
939}
940
941/// Trace all error paths, particularly for tests
942#[derive(Debug)]
943#[cfg(feature = "std")]
944pub enum TreeError<I, C = StrContext> {
945 /// Initial error that kicked things off
946 Base(TreeErrorBase<I>),
947 /// Traces added to the error while walking back up the stack
948 Stack {
949 /// Initial error that kicked things off
950 base: Box<Self>,
951 /// Traces added to the error while walking back up the stack
952 stack: Vec<TreeErrorFrame<I, C>>,
953 },
954 /// All failed branches of an `alt`
955 Alt(Vec<Self>),
956}
957
958/// See [`TreeError::Stack`]
959#[derive(Debug)]
960#[cfg(feature = "std")]
961pub enum TreeErrorFrame<I, C = StrContext> {
962 /// See [`ParserError::append`]
963 Kind(TreeErrorBase<I>),
964 /// See [`AddContext::add_context`]
965 Context(TreeErrorContext<I, C>),
966}
967
968/// See [`TreeErrorFrame::Kind`], [`ParserError::append`]
969#[derive(Debug)]
970#[cfg(feature = "std")]
971pub struct TreeErrorBase<I> {
972 /// Parsed input, at the location where the error occurred
973 pub input: I,
974 /// See [`FromExternalError::from_external_error`]
975 pub cause: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
976}
977
978/// See [`TreeErrorFrame::Context`], [`AddContext::add_context`]
979#[derive(Debug)]
980#[cfg(feature = "std")]
981pub struct TreeErrorContext<I, C = StrContext> {
982 /// Parsed input, at the location where the error occurred
983 pub input: I,
984 /// See [`AddContext::add_context`]
985 pub context: C,
986}
987
988#[cfg(feature = "std")]
989impl<I: ToOwned, C> TreeError<&I, C> {
990 /// Obtaining ownership
991 pub fn into_owned(self) -> TreeError<<I as ToOwned>::Owned, C> {
992 self.map_input(op:ToOwned::to_owned)
993 }
994}
995
996#[cfg(feature = "std")]
997impl<I, C> TreeError<I, C> {
998 /// Translate the input type
999 pub fn map_input<I2, O: Clone + Fn(I) -> I2>(self, op: O) -> TreeError<I2, C> {
1000 match self {
1001 TreeError::Base(base) => TreeError::Base(TreeErrorBase {
1002 input: op(base.input),
1003 cause: base.cause,
1004 }),
1005 TreeError::Stack { base, stack } => {
1006 let base = Box::new(base.map_input(op.clone()));
1007 let stack = stack
1008 .into_iter()
1009 .map(|frame| match frame {
1010 TreeErrorFrame::Kind(kind) => TreeErrorFrame::Kind(TreeErrorBase {
1011 input: op(kind.input),
1012 cause: kind.cause,
1013 }),
1014 TreeErrorFrame::Context(context) => {
1015 TreeErrorFrame::Context(TreeErrorContext {
1016 input: op(context.input),
1017 context: context.context,
1018 })
1019 }
1020 })
1021 .collect();
1022 TreeError::Stack { base, stack }
1023 }
1024 TreeError::Alt(alt) => {
1025 TreeError::Alt(alt.into_iter().map(|e| e.map_input(op.clone())).collect())
1026 }
1027 }
1028 }
1029
1030 fn append_frame(self, frame: TreeErrorFrame<I, C>) -> Self {
1031 match self {
1032 TreeError::Stack { base, mut stack } => {
1033 stack.push(frame);
1034 TreeError::Stack { base, stack }
1035 }
1036 base => TreeError::Stack {
1037 base: Box::new(base),
1038 stack: vec![frame],
1039 },
1040 }
1041 }
1042}
1043
1044#[cfg(feature = "std")]
1045impl<I, C> ParserError<I> for TreeError<I, C>
1046where
1047 I: Stream + Clone,
1048{
1049 type Inner = Self;
1050
1051 fn from_input(input: &I) -> Self {
1052 TreeError::Base(TreeErrorBase {
1053 input: input.clone(),
1054 cause: None,
1055 })
1056 }
1057
1058 fn append(self, input: &I, token_start: &<I as Stream>::Checkpoint) -> Self {
1059 let mut input = input.clone();
1060 input.reset(token_start);
1061 let frame = TreeErrorFrame::Kind(TreeErrorBase { input, cause: None });
1062 self.append_frame(frame)
1063 }
1064
1065 fn or(self, other: Self) -> Self {
1066 match (self, other) {
1067 (TreeError::Alt(mut first), TreeError::Alt(second)) => {
1068 // Just in case an implementation does a divide-and-conquer algorithm
1069 //
1070 // To prevent mixing `alt`s at different levels, parsers should
1071 // `alt_err.append(input)`.
1072 first.extend(second);
1073 TreeError::Alt(first)
1074 }
1075 (TreeError::Alt(mut alt), new) | (new, TreeError::Alt(mut alt)) => {
1076 alt.push(new);
1077 TreeError::Alt(alt)
1078 }
1079 (first, second) => TreeError::Alt(vec![first, second]),
1080 }
1081 }
1082
1083 #[inline(always)]
1084 fn into_inner(self) -> Result<Self::Inner, Self> {
1085 Ok(self)
1086 }
1087}
1088
1089#[cfg(feature = "std")]
1090impl<I, C> AddContext<I, C> for TreeError<I, C>
1091where
1092 I: Stream + Clone,
1093{
1094 fn add_context(self, input: &I, token_start: &<I as Stream>::Checkpoint, context: C) -> Self {
1095 let mut input: I = input.clone();
1096 input.reset(checkpoint:token_start);
1097 let frame: TreeErrorFrame = TreeErrorFrame::Context(TreeErrorContext { input, context });
1098 self.append_frame(frame)
1099 }
1100}
1101
1102#[cfg(feature = "std")]
1103#[cfg(feature = "unstable-recover")]
1104impl<I: Stream, C> FromRecoverableError<I, Self> for TreeError<I, C> {
1105 #[inline]
1106 fn from_recoverable_error(
1107 _token_start: &<I as Stream>::Checkpoint,
1108 _err_start: &<I as Stream>::Checkpoint,
1109 _input: &I,
1110 e: Self,
1111 ) -> Self {
1112 e
1113 }
1114}
1115
1116#[cfg(feature = "std")]
1117impl<I, C, E: std::error::Error + Send + Sync + 'static> FromExternalError<I, E> for TreeError<I, C>
1118where
1119 I: Clone,
1120{
1121 fn from_external_error(input: &I, e: E) -> Self {
1122 TreeError::Base(TreeErrorBase {
1123 input: input.clone(),
1124 cause: Some(Box::new(e)),
1125 })
1126 }
1127}
1128
1129#[cfg(feature = "std")]
1130impl<I, C> ErrorConvert<TreeError<(I, usize), C>> for TreeError<I, C> {
1131 #[inline]
1132 fn convert(self) -> TreeError<(I, usize), C> {
1133 self.map_input(|i: I| (i, 0))
1134 }
1135}
1136
1137#[cfg(feature = "std")]
1138impl<I, C> ErrorConvert<TreeError<I, C>> for TreeError<(I, usize), C> {
1139 #[inline]
1140 fn convert(self) -> TreeError<I, C> {
1141 self.map_input(|(i: I, _o: usize)| i)
1142 }
1143}
1144
1145#[cfg(feature = "std")]
1146impl<I, C> TreeError<I, C>
1147where
1148 I: crate::lib::std::fmt::Display,
1149 C: fmt::Display,
1150{
1151 fn write(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
1152 let child_indent = indent + 2;
1153 match self {
1154 TreeError::Base(base) => {
1155 writeln!(f, "{:indent$}{base}", "")?;
1156 }
1157 TreeError::Stack { base, stack } => {
1158 base.write(f, indent)?;
1159 for (level, frame) in stack.iter().enumerate() {
1160 match frame {
1161 TreeErrorFrame::Kind(frame) => {
1162 writeln!(f, "{:child_indent$}{level}: {frame}", "")?;
1163 }
1164 TreeErrorFrame::Context(frame) => {
1165 writeln!(f, "{:child_indent$}{level}: {frame}", "")?;
1166 }
1167 }
1168 }
1169 }
1170 TreeError::Alt(alt) => {
1171 writeln!(f, "{:indent$}during one of:", "")?;
1172 for child in alt {
1173 child.write(f, child_indent)?;
1174 }
1175 }
1176 }
1177
1178 Ok(())
1179 }
1180}
1181
1182#[cfg(feature = "std")]
1183impl<I: fmt::Display> fmt::Display for TreeErrorBase<I> {
1184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1185 if let Some(cause: &Box) = self.cause.as_ref() {
1186 write!(f, "caused by {cause}")?;
1187 }
1188 let input: String = abbreviate(self.input.to_string());
1189 write!(f, " at '{input}'")?;
1190 Ok(())
1191 }
1192}
1193
1194#[cfg(feature = "std")]
1195impl<I: fmt::Display, C: fmt::Display> fmt::Display for TreeErrorContext<I, C> {
1196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1197 let context: &C = &self.context;
1198 let input: String = abbreviate(self.input.to_string());
1199 write!(f, "{context} at '{input}'")?;
1200 Ok(())
1201 }
1202}
1203
1204#[cfg(feature = "std")]
1205impl<I: fmt::Debug + fmt::Display + Sync + Send + 'static, C: fmt::Display + fmt::Debug>
1206 std::error::Error for TreeError<I, C>
1207{
1208}
1209
1210#[cfg(feature = "std")]
1211fn abbreviate(input: String) -> String {
1212 let mut abbrev: Option<&str> = None;
1213
1214 if let Some((line: &str, _)) = input.split_once(delimiter:'\n') {
1215 abbrev = Some(line);
1216 }
1217
1218 let max_len: usize = 20;
1219 let current: &str = abbrev.unwrap_or(&input);
1220 if max_len < current.len() {
1221 if let Some((index: usize, _)) = current.char_indices().nth(max_len) {
1222 abbrev = Some(&current[..index]);
1223 }
1224 }
1225
1226 if let Some(abbrev: &str) = abbrev {
1227 format!("{abbrev}...")
1228 } else {
1229 input
1230 }
1231}
1232
1233#[cfg(feature = "std")]
1234impl<I: fmt::Display, C: fmt::Display> fmt::Display for TreeError<I, C> {
1235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1236 self.write(f, indent:0)
1237 }
1238}
1239
1240/// See [`Parser::parse`]
1241#[derive(Clone, Debug, PartialEq, Eq)]
1242pub struct ParseError<I, E> {
1243 input: I,
1244 offset: usize,
1245 inner: E,
1246}
1247
1248impl<I: Stream, E: ParserError<I>> ParseError<I, E> {
1249 pub(crate) fn new(mut input: I, start: I::Checkpoint, inner: E) -> Self {
1250 let offset: usize = input.offset_from(&start);
1251 input.reset(&start);
1252 Self {
1253 input,
1254 offset,
1255 inner,
1256 }
1257 }
1258}
1259
1260impl<I, E> ParseError<I, E> {
1261 /// The [`Stream`] at the initial location when parsing started
1262 #[inline]
1263 pub fn input(&self) -> &I {
1264 &self.input
1265 }
1266
1267 /// The location in [`ParseError::input`] where parsing failed
1268 ///
1269 /// To get the span for the `char` this points to, see [`ParseError::char_span`].
1270 ///
1271 /// <div class="warning">
1272 ///
1273 /// **Note:** This is an offset, not an index, and may point to the end of input
1274 /// (`input.len()`) on eof errors.
1275 ///
1276 /// </div>
1277 #[inline]
1278 pub fn offset(&self) -> usize {
1279 self.offset
1280 }
1281
1282 /// The original [`ParserError`]
1283 #[inline]
1284 pub fn inner(&self) -> &E {
1285 &self.inner
1286 }
1287
1288 /// The original [`ParserError`]
1289 #[inline]
1290 pub fn into_inner(self) -> E {
1291 self.inner
1292 }
1293}
1294
1295impl<I: AsBStr, E> ParseError<I, E> {
1296 /// The byte indices for the `char` at [`ParseError::offset`]
1297 #[inline]
1298 pub fn char_span(&self) -> crate::lib::std::ops::Range<usize> {
1299 char_boundary(self.input.as_bstr(), self.offset())
1300 }
1301}
1302
1303fn char_boundary(input: &[u8], offset: usize) -> crate::lib::std::ops::Range<usize> {
1304 let len = input.len();
1305 if offset == len {
1306 return offset..offset;
1307 }
1308
1309 let start = (0..(offset + 1).min(len))
1310 .rev()
1311 .find(|i| {
1312 input
1313 .get(*i)
1314 .copied()
1315 .map(is_utf8_char_boundary)
1316 .unwrap_or(false)
1317 })
1318 .unwrap_or(0);
1319 let end = (offset + 1..len)
1320 .find(|i| {
1321 input
1322 .get(*i)
1323 .copied()
1324 .map(is_utf8_char_boundary)
1325 .unwrap_or(false)
1326 })
1327 .unwrap_or(len);
1328 start..end
1329}
1330
1331/// Taken from `core::num`
1332const fn is_utf8_char_boundary(b: u8) -> bool {
1333 // This is bit magic equivalent to: b < 128 || b >= 192
1334 (b as i8) >= -0x40
1335}
1336
1337impl<I, E> core::fmt::Display for ParseError<I, E>
1338where
1339 I: AsBStr,
1340 E: core::fmt::Display,
1341{
1342 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1343 let input = self.input.as_bstr();
1344 let span_start = self.offset;
1345 let span_end = span_start;
1346 #[cfg(feature = "std")]
1347 if input.contains(&b'\n') {
1348 let (line_idx, col_idx) = translate_position(input, span_start);
1349 let line_num = line_idx + 1;
1350 let col_num = col_idx + 1;
1351 let gutter = line_num.to_string().len();
1352 let content = input
1353 .split(|c| *c == b'\n')
1354 .nth(line_idx)
1355 .expect("valid line number");
1356
1357 writeln!(f, "parse error at line {line_num}, column {col_num}")?;
1358 // |
1359 for _ in 0..gutter {
1360 write!(f, " ")?;
1361 }
1362 writeln!(f, " |")?;
1363
1364 // 1 | 00:32:00.a999999
1365 write!(f, "{line_num} | ")?;
1366 writeln!(f, "{}", String::from_utf8_lossy(content))?;
1367
1368 // | ^
1369 for _ in 0..gutter {
1370 write!(f, " ")?;
1371 }
1372 write!(f, " | ")?;
1373 for _ in 0..col_idx {
1374 write!(f, " ")?;
1375 }
1376 // The span will be empty at eof, so we need to make sure we always print at least
1377 // one `^`
1378 write!(f, "^")?;
1379 for _ in (span_start + 1)..(span_end.min(span_start + content.len())) {
1380 write!(f, "^")?;
1381 }
1382 writeln!(f)?;
1383 } else {
1384 let content = input;
1385 writeln!(f, "{}", String::from_utf8_lossy(content))?;
1386 for _ in 0..span_start {
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 }
1397 write!(f, "{}", self.inner)?;
1398
1399 Ok(())
1400 }
1401}
1402
1403#[cfg(feature = "std")]
1404fn translate_position(input: &[u8], index: usize) -> (usize, usize) {
1405 if input.is_empty() {
1406 return (0, index);
1407 }
1408
1409 let safe_index = index.min(input.len() - 1);
1410 let column_offset = index - safe_index;
1411 let index = safe_index;
1412
1413 let nl = input[0..index]
1414 .iter()
1415 .rev()
1416 .enumerate()
1417 .find(|(_, b)| **b == b'\n')
1418 .map(|(nl, _)| index - nl - 1);
1419 let line_start = match nl {
1420 Some(nl) => nl + 1,
1421 None => 0,
1422 };
1423 let line = input[0..line_start].iter().filter(|b| **b == b'\n').count();
1424
1425 // HACK: This treats byte offset and column offsets the same
1426 let column = crate::lib::std::str::from_utf8(&input[line_start..=index])
1427 .map(|s| s.chars().count() - 1)
1428 .unwrap_or_else(|_| index - line_start);
1429 let column = column + column_offset;
1430
1431 (line, column)
1432}
1433
1434#[cfg(test)]
1435mod test_char_boundary {
1436 use super::*;
1437
1438 #[test]
1439 fn ascii() {
1440 let input = "hi";
1441 let cases = [(0, 0..1), (1, 1..2), (2, 2..2)];
1442 for (offset, expected) in cases {
1443 assert_eq!(
1444 char_boundary(input.as_bytes(), offset),
1445 expected,
1446 "input={input:?}, offset={offset:?}"
1447 );
1448 }
1449 }
1450
1451 #[test]
1452 fn utf8() {
1453 let input = "βèƒôřè";
1454 assert_eq!(input.len(), 12);
1455 let cases = [
1456 (0, 0..2),
1457 (1, 0..2),
1458 (2, 2..4),
1459 (3, 2..4),
1460 (4, 4..6),
1461 (5, 4..6),
1462 (6, 6..8),
1463 (7, 6..8),
1464 (8, 8..10),
1465 (9, 8..10),
1466 (10, 10..12),
1467 (11, 10..12),
1468 (12, 12..12),
1469 ];
1470 for (offset, expected) in cases {
1471 assert_eq!(
1472 char_boundary(input.as_bytes(), offset),
1473 expected,
1474 "input={input:?}, offset={offset:?}"
1475 );
1476 }
1477 }
1478}
1479
1480#[cfg(test)]
1481#[cfg(feature = "std")]
1482mod test_parse_error {
1483 use super::*;
1484
1485 #[test]
1486 fn single_line() {
1487 let mut input = "0xZ123";
1488 let start = input.checkpoint();
1489 let _ = input.next_token().unwrap();
1490 let _ = input.next_token().unwrap();
1491 let inner = InputError::at(input);
1492 let error = ParseError::new(input, start, inner);
1493 let expected = "\
14940xZ123
1495 ^
1496failed to parse starting at: Z123";
1497 assert_eq!(error.to_string(), expected);
1498 }
1499}
1500
1501#[cfg(test)]
1502#[cfg(feature = "std")]
1503mod test_translate_position {
1504 use super::*;
1505
1506 #[test]
1507 fn empty() {
1508 let input = b"";
1509 let index = 0;
1510 let position = translate_position(&input[..], index);
1511 assert_eq!(position, (0, 0));
1512 }
1513
1514 #[test]
1515 fn start() {
1516 let input = b"Hello";
1517 let index = 0;
1518 let position = translate_position(&input[..], index);
1519 assert_eq!(position, (0, 0));
1520 }
1521
1522 #[test]
1523 fn end() {
1524 let input = b"Hello";
1525 let index = input.len() - 1;
1526 let position = translate_position(&input[..], index);
1527 assert_eq!(position, (0, input.len() - 1));
1528 }
1529
1530 #[test]
1531 fn after() {
1532 let input = b"Hello";
1533 let index = input.len();
1534 let position = translate_position(&input[..], index);
1535 assert_eq!(position, (0, input.len()));
1536 }
1537
1538 #[test]
1539 fn first_line() {
1540 let input = b"Hello\nWorld\n";
1541 let index = 2;
1542 let position = translate_position(&input[..], index);
1543 assert_eq!(position, (0, 2));
1544 }
1545
1546 #[test]
1547 fn end_of_line() {
1548 let input = b"Hello\nWorld\n";
1549 let index = 5;
1550 let position = translate_position(&input[..], index);
1551 assert_eq!(position, (0, 5));
1552 }
1553
1554 #[test]
1555 fn start_of_second_line() {
1556 let input = b"Hello\nWorld\n";
1557 let index = 6;
1558 let position = translate_position(&input[..], index);
1559 assert_eq!(position, (1, 0));
1560 }
1561
1562 #[test]
1563 fn second_line() {
1564 let input = b"Hello\nWorld\n";
1565 let index = 8;
1566 let position = translate_position(&input[..], index);
1567 assert_eq!(position, (1, 2));
1568 }
1569}
1570