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