json2pb/parser/
json.rs

1use nom::{
2    branch::alt,
3    bytes::complete::{escaped, tag, take_while, take_till1, take_while_m_n},
4    character::complete::char,
5    combinator::{cut, map, opt, value, peek},
6    error::{context, ContextError, ParseError, VerboseError, convert_error},
7    multi::separated_list0,
8    number::complete::double,
9    sequence::{delimited, preceded, separated_pair, terminated},
10    IResult,
11};
12use std::str;
13
14#[derive(Debug, PartialEq)]
15pub enum JsonValue {
16    Str(String),
17    Boolean(bool),
18    Num(f64),
19    Array(Vec<JsonValue>),
20    Object(Vec<(String, JsonValue)>),
21}
22
23fn sp<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
24    let chars = " \t\r\n";
25
26    take_while(move |c| chars.contains(c))(i)
27}
28
29fn parse_str<'a, E: ParseError<&'a str> + ContextError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
30    escaped(normal, '\\', escapable)(i)
31}
32
33fn escapable<'a, E: ParseError<&'a str> + ContextError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
34    context(
35        "escaped",
36        alt((
37            tag("\""),
38            tag("\\"),
39            tag("/"),
40            tag("b"),
41            tag("f"),
42            tag("n"),
43            tag("r"),
44            tag("t"),
45            parse_hex,
46        )),
47    )(i)
48}
49
50
51fn parse_hex<'a, E: ParseError<&'a str> + ContextError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
52    context(
53        "hex string",
54        preceded(
55            peek(tag("u")),
56            take_while_m_n(5, 5, |c: char| c.is_ascii_hexdigit() || c == 'u'),
57        ),
58    )(i)
59}
60
61fn normal<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
62    take_till1(|c: char| c == '\\' || c == '"' || c.is_ascii_control())(i)
63}
64
65fn boolean<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, bool, E> {
66    alt(
67        (
68            value(true, tag("true")),
69            value(false, tag("false")),
70            )
71    )(input)
72}
73
74fn string<'a, E: ParseError<&'a str> + ContextError<&'a str>>(
75    i: &'a str,
76) -> IResult<&'a str, &'a str, E> {
77    context(
78        "string",
79        alt((tag("\"\""), delimited(tag("\""), parse_str,tag("\"")))),
80    )(i)
81}
82
83fn array<'a, E: ParseError<&'a str> + ContextError<&'a str>>(
84    i: &'a str,
85) -> IResult<&'a str, Vec<JsonValue>, E> {
86    context(
87        "array",
88        preceded(
89            char('['),
90            cut(terminated(
91                separated_list0(preceded(sp, char(',')), json_value),
92                preceded(sp, char(']')),
93            )),
94        ),
95    )(i)
96}
97
98fn key_value<'a, E: ParseError<&'a str> + ContextError<&'a str>>(
99    i: &'a str,
100) -> IResult<&'a str, (&'a str, JsonValue), E> {
101    separated_pair(
102        preceded(sp, string),
103        cut(preceded(sp, char(':'))),
104        json_value,
105    )(i)
106}
107
108fn hash<'a, E: ParseError<&'a str> + ContextError<&'a str>>(
109    i: &'a str,
110) -> IResult<&'a str, Vec<(String, JsonValue)>, E> {
111    context(
112        "map",
113        preceded(
114            char('{'),
115            cut(terminated(
116                map(
117                    separated_list0(preceded(sp, char(',')), key_value),
118                    |tuple_vec| {
119                        tuple_vec
120                            .into_iter()
121                            .map(|(k, v)| (String::from(k), v))
122                            .collect()
123                    },
124                ),
125                preceded(sp, char('}')),
126            )),
127        ),
128    )(i)
129}
130
131/// here, we apply the space parser before trying to parse a value
132fn json_value<'a, E: ParseError<&'a str> + ContextError<&'a str>>(
133    i: &'a str,
134) -> IResult<&'a str, JsonValue, E> {
135    preceded(
136        sp,
137        alt((
138            map(hash, JsonValue::Object),
139            map(array, JsonValue::Array),
140            map(string, |s| JsonValue::Str(String::from(s))),
141            map(double, JsonValue::Num),
142            map(boolean, JsonValue::Boolean),
143        )),
144    )(i)
145}
146
147/// the root element of a JSON parser is either an object or an array
148fn root(
149    i: &str,
150) -> IResult<&str, JsonValue, VerboseError<&str>> {
151    delimited(
152        sp,
153        alt((map(hash, JsonValue::Object), map(array, JsonValue::Array))),
154        opt(sp),
155    )(i)
156}
157
158/// parse a root json object
159pub fn parse_root(i: &str) -> Result<JsonValue, Box<dyn std::error::Error + 'static>> {
160    match root(i) {
161        Ok((_, json_value)) => {
162            Ok(json_value)
163        },
164        Err(nom::Err::Error(e)) => {
165            Err(convert_error(i, e).into())
166        },
167        Err(nom::Err::Failure(e)) => {
168            Err(convert_error(i, e).into())
169        },
170        Err(nom::Err::Incomplete(_)) => {
171            Err("incomplete json".into())
172        }
173    }
174}