octane_json/
parse.rs

1use crate::value::Value;
2use std::char;
3use std::collections::HashMap;
4
5pub fn consume_ws(dat: &str) -> &str {
6    dat.trim_start()
7}
8
9pub fn parse_string(dat: &str) -> Option<(String, &str)> {
10    // Function assumes that the first character is a double quote.
11    let mut ret = String::with_capacity(dat.len());
12    let mut cur = &dat[1..];
13    while !cur.is_empty() {
14        if let Some(i) = cur.find('\\') {
15            ret.push_str(&cur[..i]);
16            let chr = cur.as_bytes()[i + 1];
17            if chr == b'u' {
18                let hex = &cur[i + 2..i + 6];
19                if hex.len() != 4 {
20                    return None;
21                }
22                if let Ok(v) = u16::from_str_radix(hex, 16) {
23                    ret.push(char::from_u32(v as u32)?);
24                    cur = &cur[i + 6..];
25                } else {
26                    return None;
27                }
28            } else {
29                let parsed = match chr {
30                    b'"' => '"',
31                    b'\\' => '\\',
32                    b'/' => '/',
33                    b'b' => '\x08',
34                    b'f' => '\x0c',
35                    b'n' => '\n',
36                    b'r' => '\r',
37                    b't' => '\t',
38                    _ => return None,
39                };
40                ret.push(parsed);
41                cur = &cur[i + 2..];
42            }
43        } else {
44            let v = cur.find('"')?;
45            ret.push_str(&cur[..v]);
46            return Some((ret, &cur[v + 1..]));
47        }
48    }
49    None
50}
51
52pub fn parse_bool(dat: &str) -> Option<(bool, &str)> {
53    if dat.starts_with("true") {
54        return Some((true, &dat[4..]));
55    } else if dat.starts_with("false") {
56        return Some((false, &dat[5..]));
57    }
58    None
59}
60
61pub fn parse_null(dat: &str) -> Option<((), &str)> {
62    if dat.starts_with("null") {
63        return Some(((), &dat[4..]));
64    }
65    None
66}
67
68pub fn parse_int_or_float(dat: &str) -> Option<(Value, &str)> {
69    let mut end = dat.len();
70    let dat_bytes = dat.as_bytes();
71    let mut is_float = false;
72    for (i, v) in dat_bytes.iter().enumerate() {
73        match v {
74            b'0'..=b'9' | b'-' | b'+' => {}
75            b'e' | b'.' => {
76                is_float = true;
77            }
78            _ => {
79                end = i;
80                break;
81            }
82        };
83    }
84    return Some((
85        if is_float {
86            Value::Float(dat[..end].parse().ok()?)
87        } else {
88            Value::Integer(dat[..end].parse().ok()?)
89        }, &dat[end..]
90    ));
91}
92
93pub fn parse_object(dat: &str) -> Option<(HashMap<String, Value>, &str)> {
94    // This function assumes that the first character is {.
95    let mut cur = consume_ws(&dat[1..]);
96    let mut ret = HashMap::<String, Value>::new();
97    if *cur.as_bytes().get(0)? == b'}' {
98        return Some((ret, &cur[1..]));
99    }
100    while !cur.is_empty() {
101        let (key, rest) = parse_string(cur)?;
102        cur = consume_ws(rest);
103        if *cur.as_bytes().get(0)? != b':' {
104            return None;
105        }
106        let (val, remainder) = parse_element(&cur[1..])?;
107        ret.insert(key, val);
108        cur = remainder;
109        match *cur.as_bytes().get(0)? {
110            b',' => {
111                cur = consume_ws(&cur[1..]);
112            }
113            b'}' => return Some((ret, &cur[1..])),
114            _ => return None,
115        }
116    }
117    None
118}
119
120pub fn parse_array(dat: &str) -> Option<(Vec<Value>, &str)> {
121    // This function assumes that the first character is [.
122    let mut cur = consume_ws(&dat[1..]);
123    let mut ret = Vec::<Value>::new();
124    if *cur.as_bytes().get(0)? == b']' {
125        return Some((ret, &cur[1..]));
126    }
127    while !cur.is_empty() {
128        let (val, rest) = parse_element(cur)?;
129        ret.push(val);
130        match *rest.as_bytes().get(0)? {
131            b',' => {
132                cur = consume_ws(&rest[1..]);
133            }
134            b']' => {
135                cur = &rest[1..];
136                break;
137            }
138            _ => return None,
139        };
140    }
141    Some((ret, cur))
142}
143
144macro_rules! do_fst {
145    (|$name:pat| $body:expr) => {
146        |($name, snd)| ($body, snd)
147    };
148    ($func:path) => {
149        |(fst, snd)| ($func(fst), snd)
150    };
151}
152
153pub fn parse_value(dat: &str) -> Option<(Value, &str)> {
154    match dat.as_bytes()[0] {
155        b'{' => parse_object(dat).map(do_fst!(Value::Object)),
156        b'-' | b'0'..=b'9' => parse_int_or_float(dat),
157        b'"' => parse_string(dat).map(do_fst!(Value::String)),
158        b't' | b'f' => parse_bool(dat).map(do_fst!(Value::Boolean)),
159        b'n' => parse_null(dat).map(do_fst!(|_| Value::Null)),
160        b'[' => parse_array(dat).map(do_fst!(Value::Array)),
161        _ => None,
162    }
163}
164
165pub fn parse_element(dat: &str) -> Option<(Value, &str)> {
166    let trimmed = consume_ws(dat);
167    let (val, rest) = parse_value(trimmed)?;
168    Some((val, consume_ws(rest)))
169}