nom/
error.rs

1//! Error management
2//!
3//! Parsers are generic over their error type, requiring that it implements
4//! the `error::ParseError<Input>` trait.
5
6use crate::internal::Parser;
7use 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`.
16pub 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, 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
39pub 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]
50pub 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)]
58pub 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
65impl<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
72impl<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
82impl<I> ContextError<I> for Error<I> {}
83
84impl<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
92impl<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")]
99impl<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
103impl<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
113impl<I> ContextError<I> for (I, ErrorKind) {}
114
115impl<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
121impl<I> ParseError<I> for () {
122  fn from_error_kind(_: I, _: ErrorKind) -> Self {}
123
124  fn append(_: I, _: ErrorKind, _: Self) -> Self {}
125}
126
127impl<I> ContextError<I> for () {}
128
129impl<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]
134pub 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
141pub 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)]
151pub 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`
161pub 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")))]
172impl<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")))]
193impl<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")))]
202impl<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")]
210impl<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, error) in &self.errors {
214      match error {
215        VerboseErrorKind::Nom(e) => writeln!(f, "{:?} at: {}", e, input)?,
216        VerboseErrorKind::Char(c) => writeln!(f, "expected '{}' at: {}", c, input)?,
217        VerboseErrorKind::Context(s) => writeln!(f, "in section '{}', at: {}", s, input)?,
218      }
219    }
220
221    Ok(())
222  }
223}
224
225#[cfg(feature = "std")]
226impl<I: fmt::Debug + fmt::Display> std::error::Error for VerboseError<I> {}
227
228use 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
233pub 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>
237where
238  F: Parser<I, O, E>,
239{
240  move |i: I| match f.parse(i.clone()) {
241    Ok(o) => Ok(o),
242    Err(Err::Incomplete(i)) => Err(Err::Incomplete(i)),
243    Err(Err::Error(e)) => Err(Err::Error(E::add_context(i, context, e))),
244    Err(Err::Failure(e)) => Err(Err::Failure(E::add_context(i, context, 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")))]
251pub 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)]
366pub 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
425pub 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
483impl 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)]
550macro_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)]
561macro_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")))]
590pub 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>
594where
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) => {
600      println!("{}: Error({:?}) at:\n{}", context, e, i.to_hex(8));
601      Err(e)
602    }
603    a => a,
604  }
605}
606
607#[cfg(test)]
608#[cfg(feature = "alloc")]
609mod 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")]
623use lib::std::{vec::Vec, collections::HashMap};
624
625#[cfg(feature = "std")]
626use lib::std::hash::Hash;
627
628#[cfg(feature = "std")]
629pub 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
638pub 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")]
646pub 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")]
659pub 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")]
670pub 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
682pub 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")]
704pub 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")]
712pub 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))]
725pub 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")]
747pub 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*/