1 | //! Error management |
2 | //! |
3 | //! Parsers are generic over their error type, requiring that it implements |
4 | //! the `error::ParseError<Input>` trait. |
5 | |
6 | use crate::internal::Parser; |
7 | use crate::lib::std::fmt; |
8 | |
9 | /// This trait must be implemented by the error type of a nom parser. |
10 | /// |
11 | /// There are already implementations of it for `(Input, ErrorKind)` |
12 | /// and `VerboseError<Input>`. |
13 | /// |
14 | /// It provides methods to create an error from some combinators, |
15 | /// and combine existing errors in combinators like `alt`. |
16 | pub trait ParseError<I>: Sized { |
17 | /// Creates an error from the input position and an [ErrorKind] |
18 | fn from_error_kind(input: I, kind: ErrorKind) -> Self; |
19 | |
20 | /// Combines an existing error with a new one created from the input |
21 | /// position and an [ErrorKind]. This is useful when backtracking |
22 | /// through a parse tree, accumulating error context on the way |
23 | fn append(input: I, kind: ErrorKind, other: Self) -> Self; |
24 | |
25 | /// Creates an error from an input position and an expected character |
26 | fn from_char(input: I, _: char) -> Self { |
27 | Self::from_error_kind(input, kind:ErrorKind::Char) |
28 | } |
29 | |
30 | /// Combines two existing errors. This function is used to compare errors |
31 | /// generated in various branches of `alt`. |
32 | fn or(self, other: Self) -> Self { |
33 | other |
34 | } |
35 | } |
36 | |
37 | /// This trait is required by the `context` combinator to add a static string |
38 | /// to an existing error |
39 | pub trait ContextError<I>: Sized { |
40 | /// Creates a new error from an input position, a static string and an existing error. |
41 | /// This is used mainly in the [context] combinator, to add user friendly information |
42 | /// to errors when backtracking through a parse tree |
43 | fn add_context(_input: I, _ctx: &'static str, other: Self) -> Self { |
44 | other |
45 | } |
46 | } |
47 | |
48 | /// This trait is required by the `map_res` combinator to integrate |
49 | /// error types from external functions, like [std::str::FromStr] |
50 | pub trait FromExternalError<I, E> { |
51 | /// Creates a new error from an input position, an [ErrorKind] indicating the |
52 | /// wrapping parser, and an external error |
53 | fn from_external_error(input: I, kind: ErrorKind, e: E) -> Self; |
54 | } |
55 | |
56 | /// default error type, only contains the error' location and code |
57 | #[derive (Debug, PartialEq)] |
58 | pub struct Error<I> { |
59 | /// position of the error in the input data |
60 | pub input: I, |
61 | /// nom error code |
62 | pub code: ErrorKind, |
63 | } |
64 | |
65 | impl<I> Error<I> { |
66 | /// creates a new basic error |
67 | pub fn new(input: I, code: ErrorKind) -> Error<I> { |
68 | Error { input, code } |
69 | } |
70 | } |
71 | |
72 | impl<I> ParseError<I> for Error<I> { |
73 | fn from_error_kind(input: I, kind: ErrorKind) -> Self { |
74 | Error { input, code: kind } |
75 | } |
76 | |
77 | fn append(_: I, _: ErrorKind, other: Self) -> Self { |
78 | other |
79 | } |
80 | } |
81 | |
82 | impl<I> ContextError<I> for Error<I> {} |
83 | |
84 | impl<I, E> FromExternalError<I, E> for Error<I> { |
85 | /// Create a new error from an input position and an external error |
86 | fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self { |
87 | Error { input, code: kind } |
88 | } |
89 | } |
90 | |
91 | /// The Display implementation allows the std::error::Error implementation |
92 | impl<I: fmt::Display> fmt::Display for Error<I> { |
93 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
94 | write!(f, "error {:?} at: {}" , self.code, self.input) |
95 | } |
96 | } |
97 | |
98 | #[cfg (feature = "std" )] |
99 | impl<I: fmt::Debug + fmt::Display> std::error::Error for Error<I> {} |
100 | |
101 | // for backward compatibility, keep those trait implementations |
102 | // for the previously used error type |
103 | impl<I> ParseError<I> for (I, ErrorKind) { |
104 | fn from_error_kind(input: I, kind: ErrorKind) -> Self { |
105 | (input, kind) |
106 | } |
107 | |
108 | fn append(_: I, _: ErrorKind, other: Self) -> Self { |
109 | other |
110 | } |
111 | } |
112 | |
113 | impl<I> ContextError<I> for (I, ErrorKind) {} |
114 | |
115 | impl<I, E> FromExternalError<I, E> for (I, ErrorKind) { |
116 | fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self { |
117 | (input, kind) |
118 | } |
119 | } |
120 | |
121 | impl<I> ParseError<I> for () { |
122 | fn from_error_kind(_: I, _: ErrorKind) -> Self {} |
123 | |
124 | fn append(_: I, _: ErrorKind, _: Self) -> Self {} |
125 | } |
126 | |
127 | impl<I> ContextError<I> for () {} |
128 | |
129 | impl<I, E> FromExternalError<I, E> for () { |
130 | fn from_external_error(_input: I, _kind: ErrorKind, _e: E) -> Self {} |
131 | } |
132 | |
133 | /// Creates an error from the input position and an [ErrorKind] |
134 | pub fn make_error<I, E: ParseError<I>>(input: I, kind: ErrorKind) -> E { |
135 | E::from_error_kind(input, kind) |
136 | } |
137 | |
138 | /// Combines an existing error with a new one created from the input |
139 | /// position and an [ErrorKind]. This is useful when backtracking |
140 | /// through a parse tree, accumulating error context on the way |
141 | pub fn append_error<I, E: ParseError<I>>(input: I, kind: ErrorKind, other: E) -> E { |
142 | E::append(input, kind, other) |
143 | } |
144 | |
145 | /// This error type accumulates errors and their position when backtracking |
146 | /// through a parse tree. With some post processing (cf `examples/json.rs`), |
147 | /// it can be used to display user friendly error messages |
148 | #[cfg (feature = "alloc" )] |
149 | #[cfg_attr (feature = "docsrs" , doc(cfg(feature = "alloc" )))] |
150 | #[derive (Clone, Debug, PartialEq)] |
151 | pub struct VerboseError<I> { |
152 | /// List of errors accumulated by `VerboseError`, containing the affected |
153 | /// part of input data, and some context |
154 | pub errors: crate::lib::std::vec::Vec<(I, VerboseErrorKind)>, |
155 | } |
156 | |
157 | #[cfg (feature = "alloc" )] |
158 | #[cfg_attr (feature = "docsrs" , doc(cfg(feature = "alloc" )))] |
159 | #[derive (Clone, Debug, PartialEq)] |
160 | /// Error context for `VerboseError` |
161 | pub enum VerboseErrorKind { |
162 | /// Static string added by the `context` function |
163 | Context(&'static str), |
164 | /// Indicates which character was expected by the `char` function |
165 | Char(char), |
166 | /// Error kind given by various nom parsers |
167 | Nom(ErrorKind), |
168 | } |
169 | |
170 | #[cfg (feature = "alloc" )] |
171 | #[cfg_attr (feature = "docsrs" , doc(cfg(feature = "alloc" )))] |
172 | impl<I> ParseError<I> for VerboseError<I> { |
173 | fn from_error_kind(input: I, kind: ErrorKind) -> Self { |
174 | VerboseError { |
175 | errors: vec![(input, VerboseErrorKind::Nom(kind))], |
176 | } |
177 | } |
178 | |
179 | fn append(input: I, kind: ErrorKind, mut other: Self) -> Self { |
180 | other.errors.push((input, VerboseErrorKind::Nom(kind))); |
181 | other |
182 | } |
183 | |
184 | fn from_char(input: I, c: char) -> Self { |
185 | VerboseError { |
186 | errors: vec![(input, VerboseErrorKind::Char(c))], |
187 | } |
188 | } |
189 | } |
190 | |
191 | #[cfg (feature = "alloc" )] |
192 | #[cfg_attr (feature = "docsrs" , doc(cfg(feature = "alloc" )))] |
193 | impl<I> ContextError<I> for VerboseError<I> { |
194 | fn add_context(input: I, ctx: &'static str, mut other: Self) -> Self { |
195 | other.errors.push((input, VerboseErrorKind::Context(ctx))); |
196 | other |
197 | } |
198 | } |
199 | |
200 | #[cfg (feature = "alloc" )] |
201 | #[cfg_attr (feature = "docsrs" , doc(cfg(feature = "alloc" )))] |
202 | impl<I, E> FromExternalError<I, E> for VerboseError<I> { |
203 | /// Create a new error from an input position and an external error |
204 | fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self { |
205 | Self::from_error_kind(input, kind) |
206 | } |
207 | } |
208 | |
209 | #[cfg (feature = "alloc" )] |
210 | impl<I: fmt::Display> fmt::Display for VerboseError<I> { |
211 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
212 | writeln!(f, "Parse error:" )?; |
213 | for (input: &I, error: &VerboseErrorKind) in &self.errors { |
214 | match error { |
215 | VerboseErrorKind::Nom(e: &ErrorKind) => writeln!(f, " {:?} at: {}" , e, input)?, |
216 | VerboseErrorKind::Char(c: &char) => writeln!(f, "expected ' {}' at: {}" , c, input)?, |
217 | VerboseErrorKind::Context(s: &&str) => writeln!(f, "in section ' {}', at: {}" , s, input)?, |
218 | } |
219 | } |
220 | |
221 | Ok(()) |
222 | } |
223 | } |
224 | |
225 | #[cfg (feature = "std" )] |
226 | impl<I: fmt::Debug + fmt::Display> std::error::Error for VerboseError<I> {} |
227 | |
228 | use crate::internal::{Err, IResult}; |
229 | |
230 | /// Create a new error from an input position, a static string and an existing error. |
231 | /// This is used mainly in the [context] combinator, to add user friendly information |
232 | /// to errors when backtracking through a parse tree |
233 | pub fn context<I: Clone, E: ContextError<I>, F, O>( |
234 | context: &'static str, |
235 | mut f: F, |
236 | ) -> impl FnMut(I) -> IResult<I, O, E> |
237 | where |
238 | F: Parser<I, O, E>, |
239 | { |
240 | move |i: I| match f.parse(input:i.clone()) { |
241 | Ok(o: (I, O)) => Ok(o), |
242 | Err(Err::Incomplete(i: Needed)) => Err(Err::Incomplete(i)), |
243 | Err(Err::Error(e: E)) => Err(Err::Error(E::add_context(_input:i, _ctx:context, other:e))), |
244 | Err(Err::Failure(e: E)) => Err(Err::Failure(E::add_context(_input:i, _ctx:context, other:e))), |
245 | } |
246 | } |
247 | |
248 | /// Transforms a `VerboseError` into a trace with input position information |
249 | #[cfg (feature = "alloc" )] |
250 | #[cfg_attr (feature = "docsrs" , doc(cfg(feature = "alloc" )))] |
251 | pub fn convert_error<I: core::ops::Deref<Target = str>>( |
252 | input: I, |
253 | e: VerboseError<I>, |
254 | ) -> crate::lib::std::string::String { |
255 | use crate::lib::std::fmt::Write; |
256 | use crate::traits::Offset; |
257 | |
258 | let mut result = crate::lib::std::string::String::new(); |
259 | |
260 | for (i, (substring, kind)) in e.errors.iter().enumerate() { |
261 | let offset = input.offset(substring); |
262 | |
263 | if input.is_empty() { |
264 | match kind { |
265 | VerboseErrorKind::Char(c) => { |
266 | write!(&mut result, " {}: expected ' {}', got empty input \n\n" , i, c) |
267 | } |
268 | VerboseErrorKind::Context(s) => write!(&mut result, " {}: in {}, got empty input \n\n" , i, s), |
269 | VerboseErrorKind::Nom(e) => write!(&mut result, " {}: in {:?}, got empty input \n\n" , i, e), |
270 | } |
271 | } else { |
272 | let prefix = &input.as_bytes()[..offset]; |
273 | |
274 | // Count the number of newlines in the first `offset` bytes of input |
275 | let line_number = prefix.iter().filter(|&&b| b == b' \n' ).count() + 1; |
276 | |
277 | // Find the line that includes the subslice: |
278 | // Find the *last* newline before the substring starts |
279 | let line_begin = prefix |
280 | .iter() |
281 | .rev() |
282 | .position(|&b| b == b' \n' ) |
283 | .map(|pos| offset - pos) |
284 | .unwrap_or(0); |
285 | |
286 | // Find the full line after that newline |
287 | let line = input[line_begin..] |
288 | .lines() |
289 | .next() |
290 | .unwrap_or(&input[line_begin..]) |
291 | .trim_end(); |
292 | |
293 | // The (1-indexed) column number is the offset of our substring into that line |
294 | let column_number = line.offset(substring) + 1; |
295 | |
296 | match kind { |
297 | VerboseErrorKind::Char(c) => { |
298 | if let Some(actual) = substring.chars().next() { |
299 | write!( |
300 | &mut result, |
301 | " {i}: at line {line_number}: \n\ |
302 | {line}\n\ |
303 | {caret:>column$}\n\ |
304 | expected ' {expected}', found {actual}\n\n" , |
305 | i = i, |
306 | line_number = line_number, |
307 | line = line, |
308 | caret = '^' , |
309 | column = column_number, |
310 | expected = c, |
311 | actual = actual, |
312 | ) |
313 | } else { |
314 | write!( |
315 | &mut result, |
316 | " {i}: at line {line_number}: \n\ |
317 | {line}\n\ |
318 | {caret:>column$}\n\ |
319 | expected ' {expected}', got end of input \n\n" , |
320 | i = i, |
321 | line_number = line_number, |
322 | line = line, |
323 | caret = '^' , |
324 | column = column_number, |
325 | expected = c, |
326 | ) |
327 | } |
328 | } |
329 | VerboseErrorKind::Context(s) => write!( |
330 | &mut result, |
331 | " {i}: at line {line_number}, in {context}: \n\ |
332 | {line}\n\ |
333 | {caret:>column$}\n\n" , |
334 | i = i, |
335 | line_number = line_number, |
336 | context = s, |
337 | line = line, |
338 | caret = '^' , |
339 | column = column_number, |
340 | ), |
341 | VerboseErrorKind::Nom(e) => write!( |
342 | &mut result, |
343 | " {i}: at line {line_number}, in {nom_err:?}: \n\ |
344 | {line}\n\ |
345 | {caret:>column$}\n\n" , |
346 | i = i, |
347 | line_number = line_number, |
348 | nom_err = e, |
349 | line = line, |
350 | caret = '^' , |
351 | column = column_number, |
352 | ), |
353 | } |
354 | } |
355 | // Because `write!` to a `String` is infallible, this `unwrap` is fine. |
356 | .unwrap(); |
357 | } |
358 | |
359 | result |
360 | } |
361 | |
362 | /// Indicates which parser returned an error |
363 | #[rustfmt::skip] |
364 | #[derive (Debug,PartialEq,Eq,Hash,Clone,Copy)] |
365 | #[allow (deprecated,missing_docs)] |
366 | pub enum ErrorKind { |
367 | Tag, |
368 | MapRes, |
369 | MapOpt, |
370 | Alt, |
371 | IsNot, |
372 | IsA, |
373 | SeparatedList, |
374 | SeparatedNonEmptyList, |
375 | Many0, |
376 | Many1, |
377 | ManyTill, |
378 | Count, |
379 | TakeUntil, |
380 | LengthValue, |
381 | TagClosure, |
382 | Alpha, |
383 | Digit, |
384 | HexDigit, |
385 | OctDigit, |
386 | AlphaNumeric, |
387 | Space, |
388 | MultiSpace, |
389 | LengthValueFn, |
390 | Eof, |
391 | Switch, |
392 | TagBits, |
393 | OneOf, |
394 | NoneOf, |
395 | Char, |
396 | CrLf, |
397 | RegexpMatch, |
398 | RegexpMatches, |
399 | RegexpFind, |
400 | RegexpCapture, |
401 | RegexpCaptures, |
402 | TakeWhile1, |
403 | Complete, |
404 | Fix, |
405 | Escaped, |
406 | EscapedTransform, |
407 | NonEmpty, |
408 | ManyMN, |
409 | Not, |
410 | Permutation, |
411 | Verify, |
412 | TakeTill1, |
413 | TakeWhileMN, |
414 | TooLarge, |
415 | Many0Count, |
416 | Many1Count, |
417 | Float, |
418 | Satisfy, |
419 | Fail, |
420 | } |
421 | |
422 | #[rustfmt::skip] |
423 | #[allow (deprecated)] |
424 | /// Converts an ErrorKind to a number |
425 | pub fn error_to_u32(e: &ErrorKind) -> u32 { |
426 | match *e { |
427 | ErrorKind::Tag => 1, |
428 | ErrorKind::MapRes => 2, |
429 | ErrorKind::MapOpt => 3, |
430 | ErrorKind::Alt => 4, |
431 | ErrorKind::IsNot => 5, |
432 | ErrorKind::IsA => 6, |
433 | ErrorKind::SeparatedList => 7, |
434 | ErrorKind::SeparatedNonEmptyList => 8, |
435 | ErrorKind::Many1 => 9, |
436 | ErrorKind::Count => 10, |
437 | ErrorKind::TakeUntil => 12, |
438 | ErrorKind::LengthValue => 15, |
439 | ErrorKind::TagClosure => 16, |
440 | ErrorKind::Alpha => 17, |
441 | ErrorKind::Digit => 18, |
442 | ErrorKind::AlphaNumeric => 19, |
443 | ErrorKind::Space => 20, |
444 | ErrorKind::MultiSpace => 21, |
445 | ErrorKind::LengthValueFn => 22, |
446 | ErrorKind::Eof => 23, |
447 | ErrorKind::Switch => 27, |
448 | ErrorKind::TagBits => 28, |
449 | ErrorKind::OneOf => 29, |
450 | ErrorKind::NoneOf => 30, |
451 | ErrorKind::Char => 40, |
452 | ErrorKind::CrLf => 41, |
453 | ErrorKind::RegexpMatch => 42, |
454 | ErrorKind::RegexpMatches => 43, |
455 | ErrorKind::RegexpFind => 44, |
456 | ErrorKind::RegexpCapture => 45, |
457 | ErrorKind::RegexpCaptures => 46, |
458 | ErrorKind::TakeWhile1 => 47, |
459 | ErrorKind::Complete => 48, |
460 | ErrorKind::Fix => 49, |
461 | ErrorKind::Escaped => 50, |
462 | ErrorKind::EscapedTransform => 51, |
463 | ErrorKind::NonEmpty => 56, |
464 | ErrorKind::ManyMN => 57, |
465 | ErrorKind::HexDigit => 59, |
466 | ErrorKind::OctDigit => 61, |
467 | ErrorKind::Many0 => 62, |
468 | ErrorKind::Not => 63, |
469 | ErrorKind::Permutation => 64, |
470 | ErrorKind::ManyTill => 65, |
471 | ErrorKind::Verify => 66, |
472 | ErrorKind::TakeTill1 => 67, |
473 | ErrorKind::TakeWhileMN => 69, |
474 | ErrorKind::TooLarge => 70, |
475 | ErrorKind::Many0Count => 71, |
476 | ErrorKind::Many1Count => 72, |
477 | ErrorKind::Float => 73, |
478 | ErrorKind::Satisfy => 74, |
479 | ErrorKind::Fail => 75, |
480 | } |
481 | } |
482 | |
483 | impl ErrorKind { |
484 | #[rustfmt::skip] |
485 | #[allow (deprecated)] |
486 | /// Converts an ErrorKind to a text description |
487 | pub fn description(&self) -> &str { |
488 | match *self { |
489 | ErrorKind::Tag => "Tag" , |
490 | ErrorKind::MapRes => "Map on Result" , |
491 | ErrorKind::MapOpt => "Map on Option" , |
492 | ErrorKind::Alt => "Alternative" , |
493 | ErrorKind::IsNot => "IsNot" , |
494 | ErrorKind::IsA => "IsA" , |
495 | ErrorKind::SeparatedList => "Separated list" , |
496 | ErrorKind::SeparatedNonEmptyList => "Separated non empty list" , |
497 | ErrorKind::Many0 => "Many0" , |
498 | ErrorKind::Many1 => "Many1" , |
499 | ErrorKind::Count => "Count" , |
500 | ErrorKind::TakeUntil => "Take until" , |
501 | ErrorKind::LengthValue => "Length followed by value" , |
502 | ErrorKind::TagClosure => "Tag closure" , |
503 | ErrorKind::Alpha => "Alphabetic" , |
504 | ErrorKind::Digit => "Digit" , |
505 | ErrorKind::AlphaNumeric => "AlphaNumeric" , |
506 | ErrorKind::Space => "Space" , |
507 | ErrorKind::MultiSpace => "Multiple spaces" , |
508 | ErrorKind::LengthValueFn => "LengthValueFn" , |
509 | ErrorKind::Eof => "End of file" , |
510 | ErrorKind::Switch => "Switch" , |
511 | ErrorKind::TagBits => "Tag on bitstream" , |
512 | ErrorKind::OneOf => "OneOf" , |
513 | ErrorKind::NoneOf => "NoneOf" , |
514 | ErrorKind::Char => "Char" , |
515 | ErrorKind::CrLf => "CrLf" , |
516 | ErrorKind::RegexpMatch => "RegexpMatch" , |
517 | ErrorKind::RegexpMatches => "RegexpMatches" , |
518 | ErrorKind::RegexpFind => "RegexpFind" , |
519 | ErrorKind::RegexpCapture => "RegexpCapture" , |
520 | ErrorKind::RegexpCaptures => "RegexpCaptures" , |
521 | ErrorKind::TakeWhile1 => "TakeWhile1" , |
522 | ErrorKind::Complete => "Complete" , |
523 | ErrorKind::Fix => "Fix" , |
524 | ErrorKind::Escaped => "Escaped" , |
525 | ErrorKind::EscapedTransform => "EscapedTransform" , |
526 | ErrorKind::NonEmpty => "NonEmpty" , |
527 | ErrorKind::ManyMN => "Many(m, n)" , |
528 | ErrorKind::HexDigit => "Hexadecimal Digit" , |
529 | ErrorKind::OctDigit => "Octal digit" , |
530 | ErrorKind::Not => "Negation" , |
531 | ErrorKind::Permutation => "Permutation" , |
532 | ErrorKind::ManyTill => "ManyTill" , |
533 | ErrorKind::Verify => "predicate verification" , |
534 | ErrorKind::TakeTill1 => "TakeTill1" , |
535 | ErrorKind::TakeWhileMN => "TakeWhileMN" , |
536 | ErrorKind::TooLarge => "Needed data size is too large" , |
537 | ErrorKind::Many0Count => "Count occurrence of >=0 patterns" , |
538 | ErrorKind::Many1Count => "Count occurrence of >=1 patterns" , |
539 | ErrorKind::Float => "Float" , |
540 | ErrorKind::Satisfy => "Satisfy" , |
541 | ErrorKind::Fail => "Fail" , |
542 | } |
543 | } |
544 | } |
545 | |
546 | /// Creates a parse error from a `nom::ErrorKind` |
547 | /// and the position in the input |
548 | #[allow (unused_variables)] |
549 | #[macro_export (local_inner_macros)] |
550 | macro_rules! error_position( |
551 | ($input:expr, $code:expr) => ({ |
552 | $crate::error::make_error($input, $code) |
553 | }); |
554 | ); |
555 | |
556 | /// Creates a parse error from a `nom::ErrorKind`, |
557 | /// the position in the input and the next error in |
558 | /// the parsing tree |
559 | #[allow (unused_variables)] |
560 | #[macro_export (local_inner_macros)] |
561 | macro_rules! error_node_position( |
562 | ($input:expr, $code:expr, $next:expr) => ({ |
563 | $crate::error::append_error($input, $code, $next) |
564 | }); |
565 | ); |
566 | |
567 | /// Prints a message and the input if the parser fails. |
568 | /// |
569 | /// The message prints the `Error` or `Incomplete` |
570 | /// and the parser's calling code. |
571 | /// |
572 | /// It also displays the input in hexdump format |
573 | /// |
574 | /// ```rust |
575 | /// use nom::{IResult, error::dbg_dmp, bytes::complete::tag}; |
576 | /// |
577 | /// fn f(i: &[u8]) -> IResult<&[u8], &[u8]> { |
578 | /// dbg_dmp(tag("abcd" ), "tag" )(i) |
579 | /// } |
580 | /// |
581 | /// let a = &b"efghijkl" [..]; |
582 | /// |
583 | /// // Will print the following message: |
584 | /// // Error(Position(0, [101, 102, 103, 104, 105, 106, 107, 108])) at l.5 by ' tag ! ( "abcd" ) ' |
585 | /// // 00000000 65 66 67 68 69 6a 6b 6c efghijkl |
586 | /// f(a); |
587 | /// ``` |
588 | #[cfg (feature = "std" )] |
589 | #[cfg_attr (feature = "docsrs" , doc(cfg(feature = "std" )))] |
590 | pub fn dbg_dmp<'a, F, O, E: std::fmt::Debug>( |
591 | f: F, |
592 | context: &'static str, |
593 | ) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], O, E> |
594 | where |
595 | F: Fn(&'a [u8]) -> IResult<&'a [u8], O, E>, |
596 | { |
597 | use crate::HexDisplay; |
598 | move |i: &'a [u8]| match f(i) { |
599 | Err(e: Err) => { |
600 | println!(" {}: Error( {:?}) at: \n{}" , context, e, i.to_hex(8)); |
601 | Err(e) |
602 | } |
603 | a: Result<(&[u8], O), Err> => a, |
604 | } |
605 | } |
606 | |
607 | #[cfg (test)] |
608 | #[cfg (feature = "alloc" )] |
609 | mod tests { |
610 | use super::*; |
611 | use crate::character::complete::char; |
612 | |
613 | #[test ] |
614 | fn convert_error_panic() { |
615 | let input = "" ; |
616 | |
617 | let _result: IResult<_, _, VerboseError<&str>> = char('x' )(input); |
618 | } |
619 | } |
620 | |
621 | /* |
622 | #[cfg(feature = "alloc")] |
623 | use lib::std::{vec::Vec, collections::HashMap}; |
624 | |
625 | #[cfg(feature = "std")] |
626 | use lib::std::hash::Hash; |
627 | |
628 | #[cfg(feature = "std")] |
629 | pub fn add_error_pattern<'a, I: Clone + Hash + Eq, O, E: Clone + Hash + Eq>( |
630 | h: &mut HashMap<VerboseError<I>, &'a str>, |
631 | e: VerboseError<I>, |
632 | message: &'a str, |
633 | ) -> bool { |
634 | h.insert(e, message); |
635 | true |
636 | } |
637 | |
638 | pub fn slice_to_offsets(input: &[u8], s: &[u8]) -> (usize, usize) { |
639 | let start = input.as_ptr(); |
640 | let off1 = s.as_ptr() as usize - start as usize; |
641 | let off2 = off1 + s.len(); |
642 | (off1, off2) |
643 | } |
644 | |
645 | #[cfg(feature = "std")] |
646 | pub fn prepare_errors<O, E: Clone>(input: &[u8], e: VerboseError<&[u8]>) -> Option<Vec<(ErrorKind, usize, usize)>> { |
647 | let mut v: Vec<(ErrorKind, usize, usize)> = Vec::new(); |
648 | |
649 | for (p, kind) in e.errors.drain(..) { |
650 | let (o1, o2) = slice_to_offsets(input, p); |
651 | v.push((kind, o1, o2)); |
652 | } |
653 | |
654 | v.reverse(); |
655 | Some(v) |
656 | } |
657 | |
658 | #[cfg(feature = "std")] |
659 | pub fn print_error<O, E: Clone>(input: &[u8], res: VerboseError<&[u8]>) { |
660 | if let Some(v) = prepare_errors(input, res) { |
661 | let colors = generate_colors(&v); |
662 | println!("parser codes: {}", print_codes(&colors, &HashMap::new())); |
663 | println!("{}", print_offsets(input, 0, &v)); |
664 | } else { |
665 | println!("not an error"); |
666 | } |
667 | } |
668 | |
669 | #[cfg(feature = "std")] |
670 | pub fn generate_colors<E>(v: &[(ErrorKind, usize, usize)]) -> HashMap<u32, u8> { |
671 | let mut h: HashMap<u32, u8> = HashMap::new(); |
672 | let mut color = 0; |
673 | |
674 | for &(ref c, _, _) in v.iter() { |
675 | h.insert(error_to_u32(c), color + 31); |
676 | color = color + 1 % 7; |
677 | } |
678 | |
679 | h |
680 | } |
681 | |
682 | pub fn code_from_offset(v: &[(ErrorKind, usize, usize)], offset: usize) -> Option<u32> { |
683 | let mut acc: Option<(u32, usize, usize)> = None; |
684 | for &(ref ek, s, e) in v.iter() { |
685 | let c = error_to_u32(ek); |
686 | if s <= offset && offset <= e { |
687 | if let Some((_, start, end)) = acc { |
688 | if start <= s && e <= end { |
689 | acc = Some((c, s, e)); |
690 | } |
691 | } else { |
692 | acc = Some((c, s, e)); |
693 | } |
694 | } |
695 | } |
696 | if let Some((code, _, _)) = acc { |
697 | return Some(code); |
698 | } else { |
699 | return None; |
700 | } |
701 | } |
702 | |
703 | #[cfg(feature = "alloc")] |
704 | pub fn reset_color(v: &mut Vec<u8>) { |
705 | v.push(0x1B); |
706 | v.push(b'['); |
707 | v.push(0); |
708 | v.push(b'm'); |
709 | } |
710 | |
711 | #[cfg(feature = "alloc")] |
712 | pub fn write_color(v: &mut Vec<u8>, color: u8) { |
713 | v.push(0x1B); |
714 | v.push(b'['); |
715 | v.push(1); |
716 | v.push(b';'); |
717 | let s = color.to_string(); |
718 | let bytes = s.as_bytes(); |
719 | v.extend(bytes.iter().cloned()); |
720 | v.push(b'm'); |
721 | } |
722 | |
723 | #[cfg(feature = "std")] |
724 | #[cfg_attr(feature = "cargo-clippy", allow(implicit_hasher))] |
725 | pub fn print_codes(colors: &HashMap<u32, u8>, names: &HashMap<u32, &str>) -> String { |
726 | let mut v = Vec::new(); |
727 | for (code, &color) in colors { |
728 | if let Some(&s) = names.get(code) { |
729 | let bytes = s.as_bytes(); |
730 | write_color(&mut v, color); |
731 | v.extend(bytes.iter().cloned()); |
732 | } else { |
733 | let s = code.to_string(); |
734 | let bytes = s.as_bytes(); |
735 | write_color(&mut v, color); |
736 | v.extend(bytes.iter().cloned()); |
737 | } |
738 | reset_color(&mut v); |
739 | v.push(b' '); |
740 | } |
741 | reset_color(&mut v); |
742 | |
743 | String::from_utf8_lossy(&v[..]).into_owned() |
744 | } |
745 | |
746 | #[cfg(feature = "std")] |
747 | pub fn print_offsets(input: &[u8], from: usize, offsets: &[(ErrorKind, usize, usize)]) -> String { |
748 | let mut v = Vec::with_capacity(input.len() * 3); |
749 | let mut i = from; |
750 | let chunk_size = 8; |
751 | let mut current_code: Option<u32> = None; |
752 | let mut current_code2: Option<u32> = None; |
753 | |
754 | let colors = generate_colors(&offsets); |
755 | |
756 | for chunk in input.chunks(chunk_size) { |
757 | let s = format!("{:08x}", i); |
758 | for &ch in s.as_bytes().iter() { |
759 | v.push(ch); |
760 | } |
761 | v.push(b'\t'); |
762 | |
763 | let mut k = i; |
764 | let mut l = i; |
765 | for &byte in chunk { |
766 | if let Some(code) = code_from_offset(&offsets, k) { |
767 | if let Some(current) = current_code { |
768 | if current != code { |
769 | reset_color(&mut v); |
770 | current_code = Some(code); |
771 | if let Some(&color) = colors.get(&code) { |
772 | write_color(&mut v, color); |
773 | } |
774 | } |
775 | } else { |
776 | current_code = Some(code); |
777 | if let Some(&color) = colors.get(&code) { |
778 | write_color(&mut v, color); |
779 | } |
780 | } |
781 | } |
782 | v.push(CHARS[(byte >> 4) as usize]); |
783 | v.push(CHARS[(byte & 0xf) as usize]); |
784 | v.push(b' '); |
785 | k = k + 1; |
786 | } |
787 | |
788 | reset_color(&mut v); |
789 | |
790 | if chunk_size > chunk.len() { |
791 | for _ in 0..(chunk_size - chunk.len()) { |
792 | v.push(b' '); |
793 | v.push(b' '); |
794 | v.push(b' '); |
795 | } |
796 | } |
797 | v.push(b'\t'); |
798 | |
799 | for &byte in chunk { |
800 | if let Some(code) = code_from_offset(&offsets, l) { |
801 | if let Some(current) = current_code2 { |
802 | if current != code { |
803 | reset_color(&mut v); |
804 | current_code2 = Some(code); |
805 | if let Some(&color) = colors.get(&code) { |
806 | write_color(&mut v, color); |
807 | } |
808 | } |
809 | } else { |
810 | current_code2 = Some(code); |
811 | if let Some(&color) = colors.get(&code) { |
812 | write_color(&mut v, color); |
813 | } |
814 | } |
815 | } |
816 | if (byte >= 32 && byte <= 126) || byte >= 128 { |
817 | v.push(byte); |
818 | } else { |
819 | v.push(b'.'); |
820 | } |
821 | l = l + 1; |
822 | } |
823 | reset_color(&mut v); |
824 | |
825 | v.push(b'\n'); |
826 | i = i + chunk_size; |
827 | } |
828 | |
829 | String::from_utf8_lossy(&v[..]).into_owned() |
830 | } |
831 | */ |
832 | |