nom_input_aux 0.1.1

Add support for managing arbitrary data during parsing with nom.
Documentation
use nom_input_aux::{InputAux, context};

use nom::{
  branch::alt,
  bytes::complete::{escaped, tag, take_while},
  character::complete::{alphanumeric1 as alphanumeric, char, one_of},
  combinator::{cut, map, opt, value},
  error::{convert_error, ContextError, ErrorKind, ParseError, VerboseError, FromExternalError},
  multi::separated_list0,
  number::complete::double,
  sequence::{delimited, preceded, separated_pair, terminated},
  Err, IResult,
};
use std::collections::HashMap;
use std::str;

#[derive(Debug, PartialEq)]
pub enum JsonValue {
  Str(String),
  Boolean(bool),
  Num(f64),
  Array(Vec<JsonValue>),
  Object(HashMap<String, JsonValue>),
}

fn sp<'a, E: ParseError<InputAux<Tracer, &'a str>>>(i: InputAux<Tracer, &'a str>) -> IResult<InputAux<Tracer, &'a str>, InputAux<Tracer, &'a str>, E> {
  let chars = " \t\r\n";

  take_while(move |c| chars.contains(c))(i)
}

fn parse_str<'a, E: ParseError<InputAux<Tracer, &'a str>>>(i: InputAux<Tracer, &'a str>) -> IResult<InputAux<Tracer, &'a str>, InputAux<Tracer, &'a str>, E> {
  escaped(alphanumeric, '\\', one_of("\"n\\"))(i)
}

fn boolean<'a, E: ParseError<InputAux<Tracer, &'a str>>>(input: InputAux<Tracer, &'a str>) -> IResult<InputAux<Tracer, &'a str>, bool, E> {
  let parse_true = value(true, tag("true"));

  let parse_false = value(false, tag("false"));

  alt((parse_true, parse_false))(input)
}

fn string<'a, E: FromExternalError<InputAux<Tracer, &'a str>, String> + ParseError<InputAux<Tracer, &'a str>> + ContextError<InputAux<Tracer, &'a str>>>(
  i: InputAux<Tracer, &'a str>,
) -> IResult<InputAux<Tracer, &'a str>, InputAux<Tracer, &'a str>, E> {
  context(
    "string",
    preceded(char('\"'), cut(terminated(parse_str, char('\"')))),
  )(i)
}

fn array<'a, E: FromExternalError<InputAux<Tracer, &'a str>, String> + ParseError<InputAux<Tracer, &'a str>> + ContextError<InputAux<Tracer, &'a str>>>(
  i: InputAux<Tracer, &'a str>,
) -> IResult<InputAux<Tracer, &'a str>, Vec<JsonValue>, E> {
  context(
    "array",
    preceded(
      char('['),
      cut(terminated(
        separated_list0(preceded(sp, char(',')), json_value),
        preceded(sp, char(']')),
      )),
    ),
  )(i)
}

fn key_value<'a, E: FromExternalError<InputAux<Tracer, &'a str>, String> + ParseError<InputAux<Tracer, &'a str>> + ContextError<InputAux<Tracer, &'a str>>>(
  i: InputAux<Tracer, &'a str>,
) -> IResult<InputAux<Tracer, &'a str>, (InputAux<Tracer, &'a str>, JsonValue), E> {
  separated_pair(
    preceded(sp, string),
    cut(preceded(sp, char(':'))),
    json_value,
  )(i)
}

fn hash<'a, E: FromExternalError<InputAux<Tracer, &'a str>, String> + ParseError<InputAux<Tracer, &'a str>> + ContextError<InputAux<Tracer, &'a str>>>(
  i: InputAux<Tracer, &'a str>,
) -> IResult<InputAux<Tracer, &'a str>, HashMap<String, JsonValue>, E> {
  context(
    "map",
    preceded(
      char('{'),
      cut(terminated(
        map(
          separated_list0(preceded(sp, char(',')), key_value),
          |tuple_vec| {
            tuple_vec
              .into_iter()
              .map(|(k, v)| (String::from(k.input), v))
              .collect()
          },
        ),
        preceded(sp, char('}')),
      )),
    ),
  )(i)
}

fn json_value<'a, E: FromExternalError<InputAux<Tracer, &'a str>, String> + ParseError<InputAux<Tracer, &'a str>> + ContextError<InputAux<Tracer, &'a str>>>(
  i: InputAux<Tracer, &'a str>,
) -> IResult<InputAux<Tracer, &'a str>, JsonValue, E> {
  preceded(
    sp,
    alt((
      map(hash, JsonValue::Object),
      map(array, JsonValue::Array),
      map(string, |s| JsonValue::Str(String::from(s.input))),
      map(double, JsonValue::Num),
      map(boolean, JsonValue::Boolean),
    )),
  )(i)
}

fn root<'a, E: FromExternalError<InputAux<Tracer, &'a str>, String> + ParseError<InputAux<Tracer, &'a str>> + ContextError<InputAux<Tracer, &'a str>>>(
  i: InputAux<Tracer, &'a str>,
) -> IResult<InputAux<Tracer, &'a str>, JsonValue, E> {
  delimited(
    sp,
    alt((map(hash, JsonValue::Object), map(array, JsonValue::Array))),
    opt(sp),
  )(i)
}

#[derive(Debug, Clone, Copy)]
struct Tracer{array: i32, object: i32, limit: i32, depth: i32}
impl Tracer {
  fn new(limit: i32) -> Self {
    Tracer{array: 0, object: 0, limit, depth: 0}
  }
}
impl nom_input_aux::ContextAware<String> for Tracer {
  fn push_context(&mut self, context: &'static str) -> Option<Result<String, String>> {
    match context {
      "array" => { println!("entering array {}", self.depth); self.array += 1; },
      "map" => { println!("entering object {}", self.depth); self.object += 1; },
      s => { println!("entering other: {} {}", self.depth, s); },
    };
    self.depth += 1;
    if self.array + self.object > self.limit {
      Some(Err("too deep".to_string()))
    } else {
      None
    }
  }
  fn pop_context<I, O, E: nom::error::FromExternalError<I, String>>(&mut self, res: &nom::IResult<I, O, E>) -> Option<Result<String, String>> {
    self.depth -= 1;
    println!("leaving {} {}", self.depth, res.is_ok());
    None
  }
}
fn main() {
  let data = "  { \"a\"\t: 42,
  \"b\": [ \"x\", \"y\", 12 ] ,
  \"c\": { \"hello\" : \"world\"
  }
  } ";

  println!(
    "will try to parse valid JSON data:\n\n**********\n{}\n**********\n",
    data
  );

  println!(
    "parsing a valid file:\n{:#?}\n",
    root::<(_, ErrorKind)>(InputAux{aux: Tracer::new(100), input: data})
  );
  println!(
    "parsing a valid file with limit:\n{:#?}\n",
    root::<(_, ErrorKind)>(InputAux{aux: Tracer::new(2), input: data})
  );

  let data = InputAux{aux: Tracer::new(100), input: "  { \"a\"\t: 42,
  \"b\": [ \"x\", \"y\", 12 ] ,
  \"c\": { 1\"hello\" : \"world\"
  }
  } "};

  println!(
    "will try to parse invalid JSON data:\n\n**********\n{:?}\n**********\n",
    data
  );

  println!(
    "basic errors - `root::<(&str, ErrorKind)>(data)`:\n{:#?}\n",
    root::<(_, ErrorKind)>(data)
  );

  println!("parsed verbose: {:#?}", root::<VerboseError<_>>(data));

  match root::<VerboseError<_>>(data) {
    Err(Err::Error(e)) | Err(Err::Failure(e)) => {
      println!(
        "verbose errors - `root::<VerboseError>(data)`:\n{:?}",
        convert_error(data, e)
      );
    }
    _ => {}
  }
}