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