grove
at @7
1//! JSON (RFC 8259): log record payloads, and later the API.
2//!
3//! Objects keep their key order, so encoding is deterministic. Integers are kept exact as i64;
4//! other numbers are f64.
5
6use std::fmt::Write as _;
7
8#[derive(Clone, Debug, PartialEq)]
9pub enum Value {
10 Null,
11 Bool(bool),
12 Int(i64),
13 Float(f64),
14 Str(String),
15 Array(Vec<Value>),
16 Object(Vec<(String, Value)>),
17}
18
19impl Value {
20 pub fn get(&self, key: &str) -> Option<&Value> {
21 match self {
22 Value::Object(fields) => fields.iter().find(|(k, _)| k == key).map(|(_, v)| v),
23 _ => None,
24 }
25 }
26 pub fn as_str(&self) -> Option<&str> {
27 match self {
28 Value::Str(s) => Some(s),
29 _ => None,
30 }
31 }
32 pub fn as_i64(&self) -> Option<i64> {
33 match self {
34 Value::Int(n) => Some(*n),
35 _ => None,
36 }
37 }
38 pub fn as_array(&self) -> Option<&[Value]> {
39 match self {
40 Value::Array(a) => Some(a),
41 _ => None,
42 }
43 }
44 pub fn is_null(&self) -> bool {
45 matches!(self, Value::Null)
46 }
47
48 pub fn encode(&self) -> String {
49 let mut out = String::new();
50 self.write(&mut out);
51 out
52 }
53
54 fn write(&self, out: &mut String) {
55 match self {
56 Value::Null => out.push_str("null"),
57 Value::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
58 Value::Int(n) => {
59 let _ = write!(out, "{n}");
60 }
61 Value::Float(f) => {
62 if f.is_finite() {
63 let _ = write!(out, "{f:?}");
64 } else {
65 out.push_str("null"); // JSON has no NaN or infinity
66 }
67 }
68 Value::Str(s) => write_str(s, out),
69 Value::Array(items) => {
70 out.push('[');
71 for (i, v) in items.iter().enumerate() {
72 if i > 0 {
73 out.push(',');
74 }
75 v.write(out);
76 }
77 out.push(']');
78 }
79 Value::Object(fields) => {
80 out.push('{');
81 for (i, (k, v)) in fields.iter().enumerate() {
82 if i > 0 {
83 out.push(',');
84 }
85 write_str(k, out);
86 out.push(':');
87 v.write(out);
88 }
89 out.push('}');
90 }
91 }
92 }
93}
94
95fn write_str(s: &str, out: &mut String) {
96 out.push('"');
97 for c in s.chars() {
98 match c {
99 '"' => out.push_str("\\\""),
100 '\\' => out.push_str("\\\\"),
101 '\n' => out.push_str("\\n"),
102 '\r' => out.push_str("\\r"),
103 '\t' => out.push_str("\\t"),
104 c if (c as u32) < 0x20 => {
105 let _ = write!(out, "\\u{:04x}", c as u32);
106 }
107 c => out.push(c),
108 }
109 }
110 out.push('"');
111}
112
113/// Build an object: `obj([("a", 1.into()), ("b", "x".into())])`.
114pub fn obj<const N: usize>(fields: [(&str, Value); N]) -> Value {
115 Value::Object(fields.into_iter().map(|(k, v)| (k.to_string(), v)).collect())
116}
117
118impl From<&str> for Value {
119 fn from(s: &str) -> Self {
120 Value::Str(s.to_string())
121 }
122}
123impl From<String> for Value {
124 fn from(s: String) -> Self {
125 Value::Str(s)
126 }
127}
128impl From<i64> for Value {
129 fn from(n: i64) -> Self {
130 Value::Int(n)
131 }
132}
133impl From<u64> for Value {
134 fn from(n: u64) -> Self {
135 Value::Int(n as i64)
136 }
137}
138impl From<bool> for Value {
139 fn from(b: bool) -> Self {
140 Value::Bool(b)
141 }
142}
143impl<T: Into<Value>> From<Option<T>> for Value {
144 fn from(o: Option<T>) -> Self {
145 o.map_or(Value::Null, Into::into)
146 }
147}
148impl From<Vec<Value>> for Value {
149 fn from(v: Vec<Value>) -> Self {
150 Value::Array(v)
151 }
152}
153
154#[derive(Debug, PartialEq)]
155pub struct ParseError {
156 pub at: usize,
157 pub msg: &'static str,
158}
159
160impl std::fmt::Display for ParseError {
161 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
162 write!(f, "invalid JSON at byte {}: {}", self.at, self.msg)
163 }
164}
165
166/// Parse a complete JSON document. Trailing content other than whitespace is an error.
167pub fn parse(src: &str) -> Result<Value, ParseError> {
168 let mut p = Parser { s: src.as_bytes(), i: 0, depth: 0 };
169 p.ws();
170 let v = p.value()?;
171 p.ws();
172 if p.i != p.s.len() {
173 return Err(p.err("trailing characters"));
174 }
175 Ok(v)
176}
177
178const MAX_DEPTH: usize = 128;
179
180struct Parser<'a> {
181 s: &'a [u8],
182 i: usize,
183 depth: usize,
184}
185
186impl Parser<'_> {
187 fn err(&self, msg: &'static str) -> ParseError {
188 ParseError { at: self.i, msg }
189 }
190 fn ws(&mut self) {
191 while let Some(b' ' | b'\n' | b'\r' | b'\t') = self.s.get(self.i) {
192 self.i += 1;
193 }
194 }
195 fn eat(&mut self, lit: &[u8]) -> bool {
196 if self.s[self.i..].starts_with(lit) {
197 self.i += lit.len();
198 true
199 } else {
200 false
201 }
202 }
203
204 fn value(&mut self) -> Result<Value, ParseError> {
205 match self.s.get(self.i) {
206 None => Err(self.err("unexpected end")),
207 Some(b'n') if self.eat(b"null") => Ok(Value::Null),
208 Some(b't') if self.eat(b"true") => Ok(Value::Bool(true)),
209 Some(b'f') if self.eat(b"false") => Ok(Value::Bool(false)),
210 Some(b'"') => Ok(Value::Str(self.string()?)),
211 Some(b'[') => self.array(),
212 Some(b'{') => self.object(),
213 Some(b'-' | b'0'..=b'9') => self.number(),
214 _ => Err(self.err("unexpected character")),
215 }
216 }
217
218 fn nest(&mut self) -> Result<(), ParseError> {
219 self.depth += 1;
220 if self.depth > MAX_DEPTH {
221 return Err(self.err("nested too deeply"));
222 }
223 Ok(())
224 }
225
226 fn array(&mut self) -> Result<Value, ParseError> {
227 self.nest()?;
228 self.i += 1;
229 let mut items = Vec::new();
230 self.ws();
231 if self.eat(b"]") {
232 self.depth -= 1;
233 return Ok(Value::Array(items));
234 }
235 loop {
236 self.ws();
237 items.push(self.value()?);
238 self.ws();
239 if self.eat(b",") {
240 continue;
241 }
242 if self.eat(b"]") {
243 self.depth -= 1;
244 return Ok(Value::Array(items));
245 }
246 return Err(self.err("expected , or ]"));
247 }
248 }
249
250 fn object(&mut self) -> Result<Value, ParseError> {
251 self.nest()?;
252 self.i += 1;
253 let mut fields: Vec<(String, Value)> = Vec::new();
254 self.ws();
255 if self.eat(b"}") {
256 self.depth -= 1;
257 return Ok(Value::Object(fields));
258 }
259 loop {
260 self.ws();
261 if self.s.get(self.i) != Some(&b'"') {
262 return Err(self.err("expected a key"));
263 }
264 let key = self.string()?;
265 if fields.iter().any(|(k, _)| *k == key) {
266 return Err(self.err("duplicate key"));
267 }
268 self.ws();
269 if !self.eat(b":") {
270 return Err(self.err("expected :"));
271 }
272 self.ws();
273 let v = self.value()?;
274 fields.push((key, v));
275 self.ws();
276 if self.eat(b",") {
277 continue;
278 }
279 if self.eat(b"}") {
280 self.depth -= 1;
281 return Ok(Value::Object(fields));
282 }
283 return Err(self.err("expected , or }"));
284 }
285 }
286
287 fn number(&mut self) -> Result<Value, ParseError> {
288 let start = self.i;
289 let digits = |p: &mut Self| {
290 let s = p.i;
291 while let Some(b'0'..=b'9') = p.s.get(p.i) {
292 p.i += 1;
293 }
294 p.i - s
295 };
296 self.eat(b"-");
297 match self.s.get(self.i) {
298 Some(b'0') => self.i += 1,
299 Some(b'1'..=b'9') => {
300 digits(self);
301 }
302 _ => return Err(self.err("expected a digit")),
303 }
304 let mut float = false;
305 if self.eat(b".") {
306 float = true;
307 if digits(self) == 0 {
308 return Err(self.err("expected a digit after ."));
309 }
310 }
311 if let Some(b'e' | b'E') = self.s.get(self.i) {
312 float = true;
313 self.i += 1;
314 if let Some(b'+' | b'-') = self.s.get(self.i) {
315 self.i += 1;
316 }
317 if digits(self) == 0 {
318 return Err(self.err("expected a digit in exponent"));
319 }
320 }
321 let text = std::str::from_utf8(&self.s[start..self.i]).expect("ascii");
322 if !float {
323 if let Ok(n) = text.parse::<i64>() {
324 return Ok(Value::Int(n));
325 }
326 }
327 text.parse::<f64>().map(Value::Float).map_err(|_| self.err("bad number"))
328 }
329
330 fn string(&mut self) -> Result<String, ParseError> {
331 self.i += 1; // opening quote
332 let mut out = String::new();
333 loop {
334 let start = self.i;
335 while let Some(&b) = self.s.get(self.i) {
336 if b == b'"' || b == b'\\' || b < 0x20 {
337 break;
338 }
339 self.i += 1;
340 }
341 // The input is a &str, so any run between ASCII delimiters is valid UTF-8.
342 out.push_str(std::str::from_utf8(&self.s[start..self.i]).map_err(|_| self.err("bad UTF-8"))?);
343 match self.s.get(self.i) {
344 None => return Err(self.err("unterminated string")),
345 Some(b'"') => {
346 self.i += 1;
347 return Ok(out);
348 }
349 Some(b'\\') => {
350 self.i += 1;
351 let c = match self.s.get(self.i) {
352 Some(b'"') => '"',
353 Some(b'\\') => '\\',
354 Some(b'/') => '/',
355 Some(b'b') => '\u{8}',
356 Some(b'f') => '\u{c}',
357 Some(b'n') => '\n',
358 Some(b'r') => '\r',
359 Some(b't') => '\t',
360 Some(b'u') => {
361 self.i += 1;
362 let hi = self.hex4()?;
363 let code = if (0xD800..0xDC00).contains(&hi) {
364 if !self.eat(b"\\u") {
365 return Err(self.err("lone surrogate"));
366 }
367 let lo = self.hex4()?;
368 if !(0xDC00..0xE000).contains(&lo) {
369 return Err(self.err("bad surrogate pair"));
370 }
371 0x10000 + ((hi - 0xD800) << 10) + (lo - 0xDC00)
372 } else if (0xDC00..0xE000).contains(&hi) {
373 return Err(self.err("lone surrogate"));
374 } else {
375 hi
376 };
377 out.push(char::from_u32(code).ok_or_else(|| self.err("bad escape"))?);
378 continue;
379 }
380 _ => return Err(self.err("bad escape")),
381 };
382 self.i += 1;
383 out.push(c);
384 }
385 Some(_) => return Err(self.err("control character in string")),
386 }
387 }
388 }
389
390 fn hex4(&mut self) -> Result<u32, ParseError> {
391 let h = self.s.get(self.i..self.i + 4).ok_or_else(|| self.err("short \\u escape"))?;
392 let s = std::str::from_utf8(h).map_err(|_| self.err("bad \\u escape"))?;
393 let n = u32::from_str_radix(s, 16).map_err(|_| self.err("bad \\u escape"))?;
394 self.i += 4;
395 Ok(n)
396 }
397}
398
399#[cfg(test)]
400mod tests {
401 use super::*;
402
403 #[test]
404 fn round_trips() {
405 let docs = [
406 r#"null"#,
407 r#"[true,false,0,-12,9007199254740993,1.5,"a"]"#,
408 r#"{"b":1,"a":{"nested":[]},"s":"quote \" backslash \\ newline \n tab \t"}"#,
409 r#""unicode: é 中文 🌱""#,
410 ];
411 for d in docs {
412 let v = parse(d).unwrap();
413 assert_eq!(parse(&v.encode()).unwrap(), v, "{d}");
414 }
415 // Key order and exact integers survive.
416 assert_eq!(parse(r#"{"z":1,"a":9007199254740993}"#).unwrap().encode(), r#"{"z":1,"a":9007199254740993}"#);
417 }
418
419 #[test]
420 fn escapes() {
421 assert_eq!(parse(r#""\u00e9\ud83c\udf31\/""#).unwrap(), Value::Str("é🌱/".into()));
422 assert_eq!(Value::Str("\u{1}".into()).encode(), r#""\u0001""#);
423 }
424
425 #[test]
426 fn rejects_invalid() {
427 for bad in [
428 "", "[", "[1,]", "{\"a\":1,}", "01", "1.", "-", "\"\\x\"", "\"\\ud800\"", "\"a\nb\"",
429 "{\"a\":1,\"a\":2}", "nul", "[1] 2", "{a:1}",
430 ] {
431 assert!(parse(bad).is_err(), "should reject {bad:?}");
432 }
433 let deep = "[".repeat(200) + &"]".repeat(200);
434 assert!(parse(&deep).is_err());
435 }
436}