Skip to main content

kui_value/
json.rs

1//! A strict JSON reader into [`Value`], for the files the core reads
2//! itself — a DTCG token file and its resolver (ADR 0062). RFC 8259 and
3//! nothing more: no comments, no trailing commas. An object keeps its keys
4//! in file order (a [`Value::Map`]), and a key given twice keeps the last
5//! value, as the DTCG resolver's "last declaration wins" reads it. A
6//! number with no fraction or exponent that fits an `i64` is an
7//! [`Value::Int`]; anything else is a [`Value::Float`].
8
9use crate::Value;
10
11/// Where a document stopped being JSON: 1-based line and column, and what
12/// was expected there.
13#[derive(Clone, Debug, PartialEq)]
14pub struct JsonError {
15    pub line: usize,
16    pub col: usize,
17    pub message: String,
18}
19
20impl std::fmt::Display for JsonError {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        write!(f, "{}:{}: {}", self.line, self.col, self.message)
23    }
24}
25
26impl std::error::Error for JsonError {}
27
28/// Nesting deeper than this is refused rather than recursed into: a token
29/// file is a few levels of groups, and a hostile one should not take the
30/// stack with it.
31const MAX_DEPTH: usize = 128;
32
33/// Reads one JSON document. Whitespace around it is fine; anything else
34/// after it is an error.
35pub fn parse(src: &str) -> Result<Value, JsonError> {
36    let mut p = Parser {
37        src: src.as_bytes(),
38        at: 0,
39    };
40    p.ws();
41    let v = p.value(0)?;
42    p.ws();
43    if p.at < p.src.len() {
44        return Err(p.err("expected the end of the document"));
45    }
46    Ok(v)
47}
48
49struct Parser<'a> {
50    src: &'a [u8],
51    at: usize,
52}
53
54impl Parser<'_> {
55    fn err(&self, message: impl Into<String>) -> JsonError {
56        let before = &self.src[..self.at.min(self.src.len())];
57        let line = before.iter().filter(|b| **b == b'\n').count() + 1;
58        let col = before.iter().rev().take_while(|b| **b != b'\n').count() + 1;
59        JsonError {
60            line,
61            col,
62            message: message.into(),
63        }
64    }
65
66    fn peek(&self) -> Option<u8> {
67        self.src.get(self.at).copied()
68    }
69
70    fn ws(&mut self) {
71        while matches!(self.peek(), Some(b' ' | b'\t' | b'\n' | b'\r')) {
72            self.at += 1;
73        }
74    }
75
76    fn eat(&mut self, lit: &str) -> Result<(), JsonError> {
77        if self.src[self.at..].starts_with(lit.as_bytes()) {
78            self.at += lit.len();
79            Ok(())
80        } else {
81            Err(self.err(format!("expected `{lit}`")))
82        }
83    }
84
85    fn value(&mut self, depth: usize) -> Result<Value, JsonError> {
86        if depth > MAX_DEPTH {
87            return Err(self.err("nested too deeply"));
88        }
89        match self.peek() {
90            Some(b'{') => self.object(depth),
91            Some(b'[') => self.array(depth),
92            Some(b'"') => Ok(Value::Str(self.string()?)),
93            Some(b't') => self.eat("true").map(|_| Value::Bool(true)),
94            Some(b'f') => self.eat("false").map(|_| Value::Bool(false)),
95            Some(b'n') => self.eat("null").map(|_| Value::Null),
96            Some(b'-' | b'0'..=b'9') => self.number(),
97            Some(_) => Err(self.err("expected a value")),
98            None => Err(self.err("expected a value, found the end")),
99        }
100    }
101
102    fn object(&mut self, depth: usize) -> Result<Value, JsonError> {
103        self.at += 1;
104        let mut out: Vec<(String, Value)> = Vec::new();
105        self.ws();
106        if self.peek() == Some(b'}') {
107            self.at += 1;
108            return Ok(Value::Map(out));
109        }
110        loop {
111            self.ws();
112            if self.peek() != Some(b'"') {
113                return Err(self.err("expected a key in double quotes"));
114            }
115            let key = self.string()?;
116            self.ws();
117            self.eat(":")?;
118            self.ws();
119            let v = self.value(depth + 1)?;
120            match out.iter_mut().find(|(k, _)| *k == key) {
121                Some(slot) => slot.1 = v,
122                None => out.push((key, v)),
123            }
124            self.ws();
125            match self.peek() {
126                Some(b',') => self.at += 1,
127                Some(b'}') => {
128                    self.at += 1;
129                    return Ok(Value::Map(out));
130                }
131                _ => return Err(self.err("expected `,` or `}`")),
132            }
133        }
134    }
135
136    fn array(&mut self, depth: usize) -> Result<Value, JsonError> {
137        self.at += 1;
138        let mut out = Vec::new();
139        self.ws();
140        if self.peek() == Some(b']') {
141            self.at += 1;
142            return Ok(Value::List(out));
143        }
144        loop {
145            self.ws();
146            out.push(self.value(depth + 1)?);
147            self.ws();
148            match self.peek() {
149                Some(b',') => self.at += 1,
150                Some(b']') => {
151                    self.at += 1;
152                    return Ok(Value::List(out));
153                }
154                _ => return Err(self.err("expected `,` or `]`")),
155            }
156        }
157    }
158
159    fn string(&mut self) -> Result<String, JsonError> {
160        self.at += 1;
161        let mut out = String::new();
162        loop {
163            let start = self.at;
164            while let Some(b) = self.peek() {
165                if b == b'"' || b == b'\\' || b < 0x20 {
166                    break;
167                }
168                self.at += 1;
169            }
170            // The run is valid UTF-8: it came from a `&str` and stops only
171            // at ASCII bytes.
172            out.push_str(std::str::from_utf8(&self.src[start..self.at]).unwrap_or(""));
173            match self.peek() {
174                Some(b'"') => {
175                    self.at += 1;
176                    return Ok(out);
177                }
178                Some(b'\\') => {
179                    self.at += 1;
180                    let c = match self.peek() {
181                        Some(b'"') => '"',
182                        Some(b'\\') => '\\',
183                        Some(b'/') => '/',
184                        Some(b'b') => '\u{8}',
185                        Some(b'f') => '\u{c}',
186                        Some(b'n') => '\n',
187                        Some(b'r') => '\r',
188                        Some(b't') => '\t',
189                        Some(b'u') => {
190                            self.at += 1;
191                            let hi = self.hex4()?;
192                            let code = if (0xD800..0xDC00).contains(&hi) {
193                                self.eat("\\u")?;
194                                let lo = self.hex4()?;
195                                if !(0xDC00..0xE000).contains(&lo) {
196                                    return Err(self.err("expected a low surrogate"));
197                                }
198                                0x10000 + ((hi - 0xD800) << 10) + (lo - 0xDC00)
199                            } else {
200                                hi
201                            };
202                            out.push(
203                                char::from_u32(code).ok_or_else(|| self.err("not a character"))?,
204                            );
205                            continue;
206                        }
207                        _ => return Err(self.err("expected an escape")),
208                    };
209                    out.push(c);
210                    self.at += 1;
211                }
212                Some(_) => return Err(self.err("a control character in a string")),
213                None => return Err(self.err("an unterminated string")),
214            }
215        }
216    }
217
218    fn hex4(&mut self) -> Result<u32, JsonError> {
219        let digits = self
220            .src
221            .get(self.at..self.at + 4)
222            .ok_or_else(|| self.err("expected four hex digits"))?;
223        let s = std::str::from_utf8(digits).map_err(|_| self.err("expected four hex digits"))?;
224        let v = u32::from_str_radix(s, 16).map_err(|_| self.err("expected four hex digits"))?;
225        self.at += 4;
226        Ok(v)
227    }
228
229    fn number(&mut self) -> Result<Value, JsonError> {
230        let start = self.at;
231        if self.peek() == Some(b'-') {
232            self.at += 1;
233        }
234        match self.peek() {
235            Some(b'0') => self.at += 1,
236            Some(b'1'..=b'9') => {
237                while matches!(self.peek(), Some(b'0'..=b'9')) {
238                    self.at += 1;
239                }
240            }
241            _ => return Err(self.err("expected a digit")),
242        }
243        let mut integral = true;
244        if self.peek() == Some(b'.') {
245            integral = false;
246            self.at += 1;
247            if !matches!(self.peek(), Some(b'0'..=b'9')) {
248                return Err(self.err("expected a digit after `.`"));
249            }
250            while matches!(self.peek(), Some(b'0'..=b'9')) {
251                self.at += 1;
252            }
253        }
254        if matches!(self.peek(), Some(b'e' | b'E')) {
255            integral = false;
256            self.at += 1;
257            if matches!(self.peek(), Some(b'+' | b'-')) {
258                self.at += 1;
259            }
260            if !matches!(self.peek(), Some(b'0'..=b'9')) {
261                return Err(self.err("expected a digit in the exponent"));
262            }
263            while matches!(self.peek(), Some(b'0'..=b'9')) {
264                self.at += 1;
265            }
266        }
267        let text = std::str::from_utf8(&self.src[start..self.at]).unwrap_or("0");
268        if integral && let Ok(i) = text.parse::<i64>() {
269            return Ok(Value::Int(i));
270        }
271        text.parse::<f64>()
272            .map(Value::Float)
273            .map_err(|_| self.err("not a number"))
274    }
275}
276
277#[cfg(test)]
278mod tests {
279    use super::*;
280
281    #[test]
282    fn reads_every_kind_and_keeps_key_order() {
283        let v = parse(r#" { "b": [1, -2.5, 3e2, true, false, null], "a": "x\né😀", "b2": {} } "#)
284            .unwrap();
285        let Value::Map(m) = &v else { panic!() };
286        assert_eq!(m[0].0, "b");
287        assert_eq!(m[1].0, "a");
288        assert_eq!(
289            m[0].1,
290            Value::List(vec![
291                Value::Int(1),
292                Value::Float(-2.5),
293                Value::Float(300.0),
294                Value::Bool(true),
295                Value::Bool(false),
296                Value::Null
297            ])
298        );
299        assert_eq!(m[1].1, Value::Str("x\né😀".into()));
300    }
301
302    #[test]
303    fn a_repeated_key_keeps_the_last_value() {
304        let v = parse(r#"{"a": 1, "a": 2}"#).unwrap();
305        assert_eq!(v, Value::Map(vec![("a".into(), Value::Int(2))]));
306    }
307
308    #[test]
309    fn errors_say_where() {
310        let e = parse("{\n  \"a\": 1,\n}").unwrap_err();
311        assert_eq!((e.line, e.col), (3, 1), "{e}");
312        assert!(parse("[1,]").is_err());
313        assert!(parse("01").is_err());
314        assert!(parse("\"a\" b").is_err());
315        assert!(
316            parse(&"[".repeat(200))
317                .unwrap_err()
318                .message
319                .contains("deeply")
320        );
321    }
322}