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: `many0`, `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 [`IResult`] |
12 | //! alias, rather than [`Result`][std::result::Result]. [`finish`][FinishIResult::finish] is |
13 | //! provided for top-level parsers to integrate with your application's error reporting. |
14 | //! |
15 | //! Error types include: |
16 | //! - `()` |
17 | //! - [`Error`] |
18 | //! - [`VerboseError`] |
19 | //! - [Custom errors][crate::_topic::error] |
20 | |
21 | #[cfg (feature = "alloc" )] |
22 | use crate::lib::std::borrow::ToOwned; |
23 | use crate::lib::std::fmt; |
24 | use core::num::NonZeroUsize; |
25 | |
26 | use crate::stream::Stream; |
27 | use crate::stream::StreamIsPartial; |
28 | #[allow (unused_imports)] // Here for intra-doc links |
29 | use crate::Parser; |
30 | |
31 | /// Holds the result of [`Parser`] |
32 | /// |
33 | /// - `Ok((I, O))` is the remaining [input][crate::stream] and the parsed value |
34 | /// - [`Err(ErrMode<E>)`][ErrMode] is the error along with how to respond to it |
35 | /// |
36 | /// By default, the error type (`E`) is [`Error`] |
37 | /// |
38 | /// At the top-level of your parser, you can use the [`FinishIResult::finish`] method to convert |
39 | /// it to a more common result type |
40 | pub type IResult<I, O, E = Error<I>> = Result<(I, O), ErrMode<E>>; |
41 | |
42 | /// Extension trait to convert a parser's [`IResult`] to a more manageable type |
43 | #[deprecated (since = "0.4.0" , note = "Replaced with `Parser::parse`" )] |
44 | pub trait FinishIResult<I, O, E> { |
45 | /// Converts the parser's [`IResult`] to a type that is more consumable by callers. |
46 | /// |
47 | /// Errors if the parser is not at the [end of input][crate::combinator::eof]. See |
48 | /// [`FinishIResult::finish_err`] if the remaining input is needed. |
49 | /// |
50 | /// # Panic |
51 | /// |
52 | /// If the result is `Err(ErrMode::Incomplete(_))`, this method will panic. |
53 | /// - **Complete parsers:** It will not be an issue, `Incomplete` is never used |
54 | /// - **Partial parsers:** `Incomplete` will be returned if there's not enough data |
55 | /// for the parser to decide, and you should gather more data before parsing again. |
56 | /// Once the parser returns either `Ok(_)`, `Err(ErrMode::Backtrack(_))` or `Err(ErrMode::Cut(_))`, |
57 | /// you can get out of the parsing loop and call `finish_err()` on the parser's result |
58 | /// |
59 | /// # Example |
60 | /// |
61 | /// ```rust |
62 | /// # #[cfg (feature = "std" )] { |
63 | /// use winnow::prelude::*; |
64 | /// use winnow::ascii::hex_uint; |
65 | /// use winnow::error::Error; |
66 | /// |
67 | /// struct Hex(u64); |
68 | /// |
69 | /// fn parse(value: &str) -> Result<Hex, Error<String>> { |
70 | /// hex_uint.map(Hex).parse_next(value).finish().map_err(Error::into_owned) |
71 | /// } |
72 | /// # } |
73 | /// ``` |
74 | #[deprecated (since = "0.4.0" , note = "Replaced with `Parser::parse`" )] |
75 | fn finish(self) -> Result<O, E>; |
76 | |
77 | /// Converts the parser's [`IResult`] to a type that is more consumable by errors. |
78 | /// |
79 | /// It keeps the same `Ok` branch, and merges `ErrMode::Backtrack` and `ErrMode::Cut` into the `Err` |
80 | /// side. |
81 | /// |
82 | /// # Panic |
83 | /// |
84 | /// If the result is `Err(ErrMode::Incomplete(_))`, this method will panic as [`ErrMode::Incomplete`] |
85 | /// should only be set when the input is [`StreamIsPartial<false>`] which this isn't implemented |
86 | /// for. |
87 | #[deprecated (since = "0.4.0" , note = "Replaced with `Parser::parse`" )] |
88 | fn finish_err(self) -> Result<(I, O), E>; |
89 | } |
90 | |
91 | #[allow (deprecated)] |
92 | impl<I, O, E> FinishIResult<I, O, E> for IResult<I, O, E> |
93 | where |
94 | I: Stream, |
95 | // Force users to deal with `Incomplete` when `StreamIsPartial<true>` |
96 | I: StreamIsPartial, |
97 | I: Clone, |
98 | E: ParseError<I>, |
99 | { |
100 | fn finish(self) -> Result<O, E> { |
101 | debug_assert!( |
102 | !I::is_partial_supported(), |
103 | "partial streams need to handle `ErrMode::Incomplete`" |
104 | ); |
105 | |
106 | let (i, o) = self.finish_err()?; |
107 | crate::combinator::eof(i).finish_err()?; |
108 | Ok(o) |
109 | } |
110 | |
111 | fn finish_err(self) -> Result<(I, O), E> { |
112 | debug_assert!( |
113 | !I::is_partial_supported(), |
114 | "partial streams need to handle `ErrMode::Incomplete`" |
115 | ); |
116 | |
117 | match self { |
118 | Ok(res) => Ok(res), |
119 | Err(ErrMode::Backtrack(e)) | Err(ErrMode::Cut(e)) => Err(e), |
120 | Err(ErrMode::Incomplete(_)) => { |
121 | panic!("complete parsers should not report `Err(ErrMode::Incomplete(_))`" ) |
122 | } |
123 | } |
124 | } |
125 | } |
126 | |
127 | /// Contains information on needed data if a parser returned `Incomplete` |
128 | /// |
129 | /// **Note:** This is only possible for `Stream` that are [partial][`StreamIsPartial`], |
130 | /// like [`Partial`][crate::Partial]. |
131 | #[derive (Debug, PartialEq, Eq, Clone, Copy)] |
132 | #[cfg_attr (nightly, warn(rustdoc::missing_doc_code_examples))] |
133 | pub enum Needed { |
134 | /// Needs more data, but we do not know how much |
135 | Unknown, |
136 | /// Contains the required data size in bytes |
137 | Size(NonZeroUsize), |
138 | } |
139 | |
140 | impl Needed { |
141 | /// Creates `Needed` instance, returns `Needed::Unknown` if the argument is zero |
142 | pub fn new(s: usize) -> Self { |
143 | match NonZeroUsize::new(s) { |
144 | Some(sz: NonZero) => Needed::Size(sz), |
145 | None => Needed::Unknown, |
146 | } |
147 | } |
148 | |
149 | /// Indicates if we know how many bytes we need |
150 | pub fn is_known(&self) -> bool { |
151 | *self != Needed::Unknown |
152 | } |
153 | |
154 | /// Maps a `Needed` to `Needed` by applying a function to a contained `Size` value. |
155 | #[inline ] |
156 | pub fn map<F: Fn(NonZeroUsize) -> usize>(self, f: F) -> Needed { |
157 | match self { |
158 | Needed::Unknown => Needed::Unknown, |
159 | Needed::Size(n: NonZero) => Needed::new(f(n)), |
160 | } |
161 | } |
162 | } |
163 | |
164 | /// The `Err` enum indicates the parser was not successful |
165 | #[derive (Debug, Clone, PartialEq)] |
166 | #[cfg_attr (nightly, warn(rustdoc::missing_doc_code_examples))] |
167 | pub enum ErrMode<E> { |
168 | /// There was not enough data to determine the appropriate action |
169 | /// |
170 | /// More data needs to be buffered before retrying the parse. |
171 | /// |
172 | /// This must only be set when the [`Stream`] is [partial][`StreamIsPartial`], like with |
173 | /// [`Partial`][crate::Partial] |
174 | /// |
175 | /// Convert this into an `Backtrack` with [`Parser::complete_err`] |
176 | Incomplete(Needed), |
177 | /// The parser failed with a recoverable error (the default). |
178 | /// |
179 | /// For example, a parser for json values might include a |
180 | /// [`dec_uint`][crate::ascii::dec_uint] as one case in an [`alt`][crate::combinator::alt] |
181 | /// combiantor. If it fails, the next case should be tried. |
182 | Backtrack(E), |
183 | /// The parser had an unrecoverable error. |
184 | /// |
185 | /// The parser was on the right branch, so directly report it to the user rather than trying |
186 | /// other branches. You can use [`cut_err()`][crate::combinator::cut_err] combinator to switch |
187 | /// from `ErrMode::Backtrack` to `ErrMode::Cut`. |
188 | /// |
189 | /// For example, one case in an [`alt`][crate::combinator::alt] combinator found a unique prefix |
190 | /// and you want any further errors parsing the case to be reported to the user. |
191 | Cut(E), |
192 | } |
193 | |
194 | impl<E> ErrMode<E> { |
195 | /// Tests if the result is Incomplete |
196 | pub fn is_incomplete(&self) -> bool { |
197 | matches!(self, ErrMode::Incomplete(_)) |
198 | } |
199 | |
200 | /// Prevent backtracking, bubbling the error up to the top |
201 | pub fn cut(self) -> Self { |
202 | match self { |
203 | ErrMode::Backtrack(e) => ErrMode::Cut(e), |
204 | rest => rest, |
205 | } |
206 | } |
207 | |
208 | /// Enable backtracking support |
209 | pub fn backtrack(self) -> Self { |
210 | match self { |
211 | ErrMode::Cut(e) => ErrMode::Backtrack(e), |
212 | rest => rest, |
213 | } |
214 | } |
215 | |
216 | /// Applies the given function to the inner error |
217 | pub fn map<E2, F>(self, f: F) -> ErrMode<E2> |
218 | where |
219 | F: FnOnce(E) -> E2, |
220 | { |
221 | match self { |
222 | ErrMode::Incomplete(n) => ErrMode::Incomplete(n), |
223 | ErrMode::Cut(t) => ErrMode::Cut(f(t)), |
224 | ErrMode::Backtrack(t) => ErrMode::Backtrack(f(t)), |
225 | } |
226 | } |
227 | |
228 | /// Automatically converts between errors if the underlying type supports it |
229 | pub fn convert<F>(self) -> ErrMode<F> |
230 | where |
231 | E: ErrorConvert<F>, |
232 | { |
233 | self.map(ErrorConvert::convert) |
234 | } |
235 | } |
236 | |
237 | impl<I, E: ParseError<I>> ParseError<I> for ErrMode<E> { |
238 | fn from_error_kind(input: I, kind: ErrorKind) -> Self { |
239 | ErrMode::Backtrack(E::from_error_kind(input, kind)) |
240 | } |
241 | |
242 | #[cfg_attr (debug_assertions, track_caller)] |
243 | fn assert(input: I, message: &'static str) -> Self |
244 | where |
245 | I: crate::lib::std::fmt::Debug, |
246 | { |
247 | ErrMode::Backtrack(E::assert(input, message)) |
248 | } |
249 | |
250 | fn append(self, input: I, kind: ErrorKind) -> Self { |
251 | match self { |
252 | ErrMode::Backtrack(e) => ErrMode::Backtrack(e.append(input, kind)), |
253 | e => e, |
254 | } |
255 | } |
256 | |
257 | fn or(self, other: Self) -> Self { |
258 | match (self, other) { |
259 | (ErrMode::Backtrack(e), ErrMode::Backtrack(o)) => ErrMode::Backtrack(e.or(o)), |
260 | (ErrMode::Incomplete(e), _) | (_, ErrMode::Incomplete(e)) => ErrMode::Incomplete(e), |
261 | (ErrMode::Cut(e), _) | (_, ErrMode::Cut(e)) => ErrMode::Cut(e), |
262 | } |
263 | } |
264 | } |
265 | |
266 | impl<I, EXT, E> FromExternalError<I, EXT> for ErrMode<E> |
267 | where |
268 | E: FromExternalError<I, EXT>, |
269 | { |
270 | fn from_external_error(input: I, kind: ErrorKind, e: EXT) -> Self { |
271 | ErrMode::Backtrack(E::from_external_error(input, kind, e)) |
272 | } |
273 | } |
274 | |
275 | impl<T> ErrMode<Error<T>> { |
276 | /// Maps `ErrMode<Error<T>>` to `ErrMode<Error<U>>` with the given `F: T -> U` |
277 | pub fn map_input<U, F>(self, f: F) -> ErrMode<Error<U>> |
278 | where |
279 | F: FnOnce(T) -> U, |
280 | { |
281 | match self { |
282 | ErrMode::Incomplete(n: Needed) => ErrMode::Incomplete(n), |
283 | ErrMode::Cut(Error { input: T, kind: ErrorKind }) => ErrMode::Cut(Error { |
284 | input: f(input), |
285 | kind, |
286 | }), |
287 | ErrMode::Backtrack(Error { input: T, kind: ErrorKind }) => ErrMode::Backtrack(Error { |
288 | input: f(input), |
289 | kind, |
290 | }), |
291 | } |
292 | } |
293 | } |
294 | |
295 | impl<E: Eq> Eq for ErrMode<E> {} |
296 | |
297 | impl<E> fmt::Display for ErrMode<E> |
298 | where |
299 | E: fmt::Debug, |
300 | { |
301 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
302 | match self { |
303 | ErrMode::Incomplete(Needed::Size(u: &NonZero)) => write!(f, "Parsing requires {} bytes/chars" , u), |
304 | ErrMode::Incomplete(Needed::Unknown) => write!(f, "Parsing requires more data" ), |
305 | ErrMode::Cut(c: &E) => write!(f, "Parsing Failure: {:?}" , c), |
306 | ErrMode::Backtrack(c: &E) => write!(f, "Parsing Error: {:?}" , c), |
307 | } |
308 | } |
309 | } |
310 | |
311 | /// The basic [`Parser`] trait for errors |
312 | /// |
313 | /// It provides methods to create an error from some combinators, |
314 | /// and combine existing errors in combinators like `alt`. |
315 | pub trait ParseError<I>: Sized { |
316 | /// Creates an error from the input position and an [`ErrorKind`] |
317 | fn from_error_kind(input: I, kind: ErrorKind) -> Self; |
318 | |
319 | /// Process a parser assertion |
320 | #[cfg_attr (debug_assertions, track_caller)] |
321 | fn assert(input: I, _message: &'static str) -> Self |
322 | where |
323 | I: crate::lib::std::fmt::Debug, |
324 | { |
325 | #[cfg (debug_assertions)] |
326 | panic!("assert ` {}` failed at {:#?}" , _message, input); |
327 | #[cfg (not(debug_assertions))] |
328 | Self::from_error_kind(input, ErrorKind::Assert) |
329 | } |
330 | |
331 | /// Like [`ParseError::from_error_kind`] but merges it with the existing error. |
332 | /// |
333 | /// This is useful when backtracking through a parse tree, accumulating error context on the |
334 | /// way. |
335 | fn append(self, input: I, kind: ErrorKind) -> Self; |
336 | |
337 | /// Combines errors from two different parse branches. |
338 | /// |
339 | /// For example, this would be used by [`alt`][crate::combinator::alt] to report the error from |
340 | /// each case. |
341 | fn or(self, other: Self) -> Self { |
342 | other |
343 | } |
344 | } |
345 | |
346 | /// Used by [`Parser::context`] to add custom data to error while backtracking |
347 | /// |
348 | /// May be implemented multiple times for different kinds of context. |
349 | pub trait ContextError<I, C = &'static str>: Sized { |
350 | /// Append to an existing error custom data |
351 | /// |
352 | /// This is used mainly by [`Parser::context`], to add user friendly information |
353 | /// to errors when backtracking through a parse tree |
354 | fn add_context(self, _input: I, _ctx: C) -> Self { |
355 | self |
356 | } |
357 | } |
358 | |
359 | /// Create a new error with an external error, from [`std::str::FromStr`] |
360 | /// |
361 | /// This trait is required by the [`Parser::try_map`] combinator. |
362 | pub trait FromExternalError<I, E> { |
363 | /// Like [`ParseError::from_error_kind`] but also include an external error. |
364 | fn from_external_error(input: I, kind: ErrorKind, e: E) -> Self; |
365 | } |
366 | |
367 | /// Equivalent of `From` implementation to avoid orphan rules in bits parsers |
368 | pub trait ErrorConvert<E> { |
369 | /// Transform to another error type |
370 | fn convert(self) -> E; |
371 | } |
372 | |
373 | /// Default error type, only contains the error' location and kind |
374 | /// |
375 | /// This is a low-overhead error that only provides basic information. For less overhead, see |
376 | /// `()`. Fore more information, see [`VerboseError`]. |
377 | ///:w |
378 | /// **Note:** [context][Parser::context] and inner errors (like from [`Parser::try_map`]) will be |
379 | /// dropped. |
380 | #[derive (Copy, Clone, Debug, Eq, PartialEq)] |
381 | pub struct Error<I> { |
382 | /// The input stream, pointing to the location where the error occurred |
383 | pub input: I, |
384 | /// A rudimentary error kind |
385 | pub kind: ErrorKind, |
386 | } |
387 | |
388 | impl<I> Error<I> { |
389 | /// Creates a new basic error |
390 | pub fn new(input: I, kind: ErrorKind) -> Error<I> { |
391 | Error { input, kind } |
392 | } |
393 | } |
394 | |
395 | #[cfg (feature = "alloc" )] |
396 | impl<'i, I: ToOwned + ?Sized> Error<&'i I> { |
397 | /// Obtaining ownership |
398 | pub fn into_owned(self) -> Error<<I as ToOwned>::Owned> { |
399 | Error { |
400 | input: self.input.to_owned(), |
401 | kind: self.kind, |
402 | } |
403 | } |
404 | } |
405 | |
406 | impl<I> ParseError<I> for Error<I> { |
407 | fn from_error_kind(input: I, kind: ErrorKind) -> Self { |
408 | Error { input, kind } |
409 | } |
410 | |
411 | fn append(self, _: I, _: ErrorKind) -> Self { |
412 | self |
413 | } |
414 | } |
415 | |
416 | impl<I, C> ContextError<I, C> for Error<I> {} |
417 | |
418 | impl<I, E> FromExternalError<I, E> for Error<I> { |
419 | /// Create a new error from an input position and an external error |
420 | fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self { |
421 | Error { input, kind } |
422 | } |
423 | } |
424 | |
425 | impl<I> ErrorConvert<Error<(I, usize)>> for Error<I> { |
426 | fn convert(self) -> Error<(I, usize)> { |
427 | Error { |
428 | input: (self.input, 0), |
429 | kind: self.kind, |
430 | } |
431 | } |
432 | } |
433 | |
434 | impl<I> ErrorConvert<Error<I>> for Error<(I, usize)> { |
435 | fn convert(self) -> Error<I> { |
436 | Error { |
437 | input: self.input.0, |
438 | kind: self.kind, |
439 | } |
440 | } |
441 | } |
442 | |
443 | /// The Display implementation allows the `std::error::Error` implementation |
444 | impl<I: fmt::Display> fmt::Display for Error<I> { |
445 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
446 | write!(f, "error {:?} at: {}" , self.kind, self.input) |
447 | } |
448 | } |
449 | |
450 | #[cfg (feature = "std" )] |
451 | impl<I: fmt::Debug + fmt::Display + Sync + Send + 'static> std::error::Error for Error<I> {} |
452 | |
453 | impl<I> ParseError<I> for () { |
454 | fn from_error_kind(_: I, _: ErrorKind) -> Self {} |
455 | |
456 | fn append(self, _: I, _: ErrorKind) -> Self {} |
457 | } |
458 | |
459 | impl<I, C> ContextError<I, C> for () {} |
460 | |
461 | impl<I, E> FromExternalError<I, E> for () { |
462 | fn from_external_error(_input: I, _kind: ErrorKind, _e: E) -> Self {} |
463 | } |
464 | |
465 | impl ErrorConvert<()> for () { |
466 | fn convert(self) {} |
467 | } |
468 | |
469 | /// Accumulates error information while backtracking |
470 | /// |
471 | /// For less overhead (and information), see [`Error`]. |
472 | /// |
473 | /// [`convert_error`] provides an example of how to render this for end-users. |
474 | /// |
475 | /// **Note:** This will only capture the last failed branch for combinators like |
476 | /// [`alt`][crate::combinator::alt]. |
477 | #[cfg (feature = "alloc" )] |
478 | #[derive (Clone, Debug, Eq, PartialEq)] |
479 | pub struct VerboseError<I> { |
480 | /// Accumulated error information |
481 | pub errors: crate::lib::std::vec::Vec<(I, VerboseErrorKind)>, |
482 | } |
483 | |
484 | #[cfg (feature = "alloc" )] |
485 | impl<'i, I: ToOwned + ?Sized> VerboseError<&'i I> { |
486 | /// Obtaining ownership |
487 | pub fn into_owned(self) -> VerboseError<<I as ToOwned>::Owned> { |
488 | #[allow (clippy::redundant_clone)] // false positive |
489 | VerboseError { |
490 | errors: self |
491 | .errors |
492 | .into_iter() |
493 | .map(|(i: &I, k: VerboseErrorKind)| (i.to_owned(), k)) |
494 | .collect(), |
495 | } |
496 | } |
497 | } |
498 | |
499 | #[cfg (feature = "alloc" )] |
500 | #[derive (Clone, Debug, Eq, PartialEq)] |
501 | /// Error context for `VerboseError` |
502 | pub enum VerboseErrorKind { |
503 | /// Static string added by the `context` function |
504 | Context(&'static str), |
505 | /// Error kind given by various parsers |
506 | Winnow(ErrorKind), |
507 | } |
508 | |
509 | #[cfg (feature = "alloc" )] |
510 | impl<I> ParseError<I> for VerboseError<I> { |
511 | fn from_error_kind(input: I, kind: ErrorKind) -> Self { |
512 | VerboseError { |
513 | errors: vec![(input, VerboseErrorKind::Winnow(kind))], |
514 | } |
515 | } |
516 | |
517 | fn append(mut self, input: I, kind: ErrorKind) -> Self { |
518 | self.errors.push((input, VerboseErrorKind::Winnow(kind))); |
519 | self |
520 | } |
521 | } |
522 | |
523 | #[cfg (feature = "alloc" )] |
524 | impl<I> ContextError<I, &'static str> for VerboseError<I> { |
525 | fn add_context(mut self, input: I, ctx: &'static str) -> Self { |
526 | self.errors.push((input, VerboseErrorKind::Context(ctx))); |
527 | self |
528 | } |
529 | } |
530 | |
531 | #[cfg (feature = "alloc" )] |
532 | impl<I, E> FromExternalError<I, E> for VerboseError<I> { |
533 | /// Create a new error from an input position and an external error |
534 | fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self { |
535 | Self::from_error_kind(input, kind) |
536 | } |
537 | } |
538 | |
539 | #[cfg (feature = "alloc" )] |
540 | impl<I> ErrorConvert<VerboseError<I>> for VerboseError<(I, usize)> { |
541 | fn convert(self) -> VerboseError<I> { |
542 | VerboseError { |
543 | errors: self.errors.into_iter().map(|(i: (I, usize), e: VerboseErrorKind)| (i.0, e)).collect(), |
544 | } |
545 | } |
546 | } |
547 | |
548 | #[cfg (feature = "alloc" )] |
549 | impl<I> ErrorConvert<VerboseError<(I, usize)>> for VerboseError<I> { |
550 | fn convert(self) -> VerboseError<(I, usize)> { |
551 | VerboseError { |
552 | errors: self.errors.into_iter().map(|(i: I, e: VerboseErrorKind)| ((i, 0), e)).collect(), |
553 | } |
554 | } |
555 | } |
556 | |
557 | #[cfg (feature = "alloc" )] |
558 | impl<I: fmt::Display> fmt::Display for VerboseError<I> { |
559 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
560 | writeln!(f, "Parse error:" )?; |
561 | for (input: &I, error: &VerboseErrorKind) in &self.errors { |
562 | match error { |
563 | VerboseErrorKind::Winnow(e: &ErrorKind) => writeln!(f, " {:?} at: {}" , e, input)?, |
564 | VerboseErrorKind::Context(s: &&str) => writeln!(f, "in section ' {}', at: {}" , s, input)?, |
565 | } |
566 | } |
567 | |
568 | Ok(()) |
569 | } |
570 | } |
571 | |
572 | #[cfg (feature = "std" )] |
573 | impl<I: fmt::Debug + fmt::Display + Sync + Send + 'static> std::error::Error for VerboseError<I> {} |
574 | |
575 | /// Transforms a `VerboseError` into a trace with input position information |
576 | #[cfg (feature = "alloc" )] |
577 | pub fn convert_error<I: core::ops::Deref<Target = str>>( |
578 | input: I, |
579 | e: VerboseError<I>, |
580 | ) -> crate::lib::std::string::String { |
581 | use crate::lib::std::fmt::Write; |
582 | use crate::stream::Offset; |
583 | |
584 | let mut result = crate::lib::std::string::String::new(); |
585 | |
586 | for (i, (substring, kind)) in e.errors.iter().enumerate() { |
587 | let offset = input.offset_to(substring); |
588 | |
589 | if input.is_empty() { |
590 | match kind { |
591 | VerboseErrorKind::Context(s) => { |
592 | write!(&mut result, " {}: in {}, got empty input \n\n" , i, s) |
593 | } |
594 | VerboseErrorKind::Winnow(e) => { |
595 | write!(&mut result, " {}: in {:?}, got empty input \n\n" , i, e) |
596 | } |
597 | } |
598 | } else { |
599 | let prefix = &input.as_bytes()[..offset]; |
600 | |
601 | // Count the number of newlines in the first `offset` bytes of input |
602 | let line_number = prefix.iter().filter(|&&b| b == b' \n' ).count() + 1; |
603 | |
604 | // Find the line that includes the subslice: |
605 | // Find the *last* newline before the substring starts |
606 | let line_begin = prefix |
607 | .iter() |
608 | .rev() |
609 | .position(|&b| b == b' \n' ) |
610 | .map(|pos| offset - pos) |
611 | .unwrap_or(0); |
612 | |
613 | // Find the full line after that newline |
614 | let line = input[line_begin..] |
615 | .lines() |
616 | .next() |
617 | .unwrap_or(&input[line_begin..]) |
618 | .trim_end(); |
619 | |
620 | // The (1-indexed) column number is the offset of our substring into that line |
621 | let column_number = line.offset_to(substring) + 1; |
622 | |
623 | match kind { |
624 | VerboseErrorKind::Context(s) => write!( |
625 | &mut result, |
626 | " {i}: at line {line_number}, in {context}: \n\ |
627 | {line}\n\ |
628 | {caret:>column$}\n\n" , |
629 | i = i, |
630 | line_number = line_number, |
631 | context = s, |
632 | line = line, |
633 | caret = '^' , |
634 | column = column_number, |
635 | ), |
636 | VerboseErrorKind::Winnow(e) => write!( |
637 | &mut result, |
638 | " {i}: at line {line_number}, in {kind:?}: \n\ |
639 | {line}\n\ |
640 | {caret:>column$}\n\n" , |
641 | i = i, |
642 | line_number = line_number, |
643 | kind = e, |
644 | line = line, |
645 | caret = '^' , |
646 | column = column_number, |
647 | ), |
648 | } |
649 | } |
650 | // Because `write!` to a `String` is infallible, this `unwrap` is fine. |
651 | .unwrap(); |
652 | } |
653 | |
654 | result |
655 | } |
656 | |
657 | /// Provide some minor debug context for errors |
658 | #[rustfmt::skip] |
659 | #[derive (Debug,PartialEq,Eq,Hash,Clone,Copy)] |
660 | #[allow (missing_docs)] |
661 | pub enum ErrorKind { |
662 | Assert, |
663 | Token, |
664 | Tag, |
665 | Alt, |
666 | Many, |
667 | Eof, |
668 | Slice, |
669 | Complete, |
670 | Not, |
671 | Verify, |
672 | Fail, |
673 | } |
674 | |
675 | impl ErrorKind { |
676 | #[rustfmt::skip] |
677 | /// Converts an `ErrorKind` to a text description |
678 | pub fn description(&self) -> &str { |
679 | match *self { |
680 | ErrorKind::Assert => "assert" , |
681 | ErrorKind::Token => "token" , |
682 | ErrorKind::Tag => "tag" , |
683 | ErrorKind::Alt => "alternative" , |
684 | ErrorKind::Many => "many" , |
685 | ErrorKind::Eof => "end of file" , |
686 | ErrorKind::Slice => "slice" , |
687 | ErrorKind::Complete => "complete" , |
688 | ErrorKind::Not => "negation" , |
689 | ErrorKind::Verify => "predicate verification" , |
690 | ErrorKind::Fail => "fail" , |
691 | } |
692 | } |
693 | } |
694 | |
695 | /// Creates a parse error from a [`ErrorKind`] |
696 | /// and the position in the input |
697 | #[cfg (test)] |
698 | macro_rules! error_position( |
699 | ($input:expr, $code:expr) => ({ |
700 | $crate::error::ParseError::from_error_kind($input, $code) |
701 | }); |
702 | ); |
703 | |
704 | #[cfg (test)] |
705 | macro_rules! error_node_position( |
706 | ($input:expr, $code:expr, $next:expr) => ({ |
707 | $crate::error::ParseError::append($next, $input, $code) |
708 | }); |
709 | ); |
710 | |
711 | #[cfg (test)] |
712 | #[cfg (feature = "alloc" )] |
713 | mod tests { |
714 | use super::*; |
715 | |
716 | #[test ] |
717 | fn convert_error_panic() { |
718 | let input = "" ; |
719 | |
720 | let _result: IResult<_, _, VerboseError<&str>> = 'x' .parse_next(input); |
721 | } |
722 | } |
723 | |