1use std::collections::BTreeMap;
10mod output;
11use output::Output;
12
13#[derive(Debug, Clone, PartialEq)]
15pub enum Json {
16 Null,
18 Bool(bool),
20 Num(f64),
22 Str(String),
24 Arr(Vec<Json>),
26 Obj(BTreeMap<String, Json>),
28}
29
30impl Json {
31 pub fn obj() -> Json {
33 Json::Obj(BTreeMap::new())
34 }
35
36 pub fn set(&mut self, key: &str, value: Json) {
38 if let Json::Obj(map) = self {
39 map.insert(key.to_string(), value);
40 }
41 }
42
43 pub fn get(&self, key: &str) -> Option<&Json> {
45 match self {
46 Json::Obj(map) => map.get(key),
47 _ => None,
48 }
49 }
50
51 pub fn as_str(&self) -> Option<&str> {
53 match self {
54 Json::Str(s) => Some(s),
55 _ => None,
56 }
57 }
58
59 pub fn as_string(&self) -> Option<String> {
61 self.as_str().map(|s| s.to_string())
62 }
63
64 pub fn as_f64(&self) -> Option<f64> {
66 match self {
67 Json::Num(n) => Some(*n),
68 _ => None,
69 }
70 }
71
72 pub fn as_u64(&self) -> Option<u64> {
75 self.as_f64().and_then(|n| {
76 if n.is_finite() && n >= 0.0 && n < u64::MAX as f64 && n.fract() == 0.0 {
78 Some(n as u64)
79 } else {
80 None
81 }
82 })
83 }
84
85 pub fn as_u32(&self) -> Option<u32> {
88 self.as_u64().and_then(|n| u32::try_from(n).ok())
89 }
90
91 pub fn as_bool(&self) -> Option<bool> {
93 match self {
94 Json::Bool(b) => Some(*b),
95 _ => None,
96 }
97 }
98
99 pub fn as_arr(&self) -> Option<&[Json]> {
101 match self {
102 Json::Arr(v) => Some(v),
103 _ => None,
104 }
105 }
106
107 pub fn field_str(&self, key: &str) -> Option<String> {
109 self.get(key).and_then(|v| v.as_string())
110 }
111
112 pub fn field_u64(&self, key: &str) -> Option<u64> {
114 self.get(key).and_then(|v| v.as_u64())
115 }
116
117 pub fn field_u32(&self, key: &str) -> Option<u32> {
119 self.get(key).and_then(|v| v.as_u32())
120 }
121
122 pub fn field_f64(&self, key: &str) -> Option<f64> {
124 self.get(key).and_then(|v| v.as_f64())
125 }
126
127 pub fn field_optional_bool(&self, key: &str) -> Option<bool> {
133 self.get(key).and_then(|v| v.as_bool())
134 }
135
136 pub fn field_bool(&self, key: &str) -> bool {
139 self.get(key).and_then(|v| v.as_bool()).unwrap_or(false)
140 }
141}
142
143impl From<&str> for Json {
144 fn from(s: &str) -> Json {
145 Json::Str(s.to_string())
146 }
147}
148impl From<String> for Json {
149 fn from(s: String) -> Json {
150 Json::Str(s)
151 }
152}
153impl From<u64> for Json {
154 fn from(n: u64) -> Json {
155 Json::Num(n as f64)
156 }
157}
158impl From<u32> for Json {
159 fn from(n: u32) -> Json {
160 Json::Num(n as f64)
161 }
162}
163impl From<usize> for Json {
164 fn from(n: usize) -> Json {
165 Json::Num(n as f64)
166 }
167}
168impl From<i64> for Json {
169 fn from(n: i64) -> Json {
170 Json::Num(n as f64)
171 }
172}
173impl From<f64> for Json {
174 fn from(n: f64) -> Json {
175 Json::Num(n)
176 }
177}
178impl From<bool> for Json {
179 fn from(b: bool) -> Json {
180 Json::Bool(b)
181 }
182}
183impl<T: Into<Json>> From<Option<T>> for Json {
184 fn from(v: Option<T>) -> Json {
185 match v {
186 Some(x) => x.into(),
187 None => Json::Null,
188 }
189 }
190}
191impl<T: Into<Json>> From<Vec<T>> for Json {
192 fn from(v: Vec<T>) -> Json {
193 Json::Arr(v.into_iter().map(Into::into).collect())
194 }
195}
196
197impl Json {
202 pub fn to_string_compact(&self) -> String {
204 let mut out = String::new();
205 self.write(&mut out, None, 0);
206 out
207 }
208
209 pub fn to_string_pretty(&self) -> String {
211 let mut out = String::new();
212 self.write(&mut out, Some(2), 0);
213 out
214 }
215
216 pub(crate) fn to_string_pretty_bounded(
219 &self,
220 maximum: usize,
221 ) -> Result<String, std::fmt::Error> {
222 let mut out = output::Limited::new(maximum);
223 self.write(&mut out, Some(2), 0);
224 out.finish()
225 }
226
227 pub(crate) fn check_pretty_size(&self, maximum: usize) -> std::fmt::Result {
228 let mut out = output::Counter::new(maximum);
229 self.write(&mut out, Some(2), 0);
230 out.finish()
231 }
232
233 fn write(&self, out: &mut impl Output, indent: Option<usize>, depth: usize) {
234 if out.exhausted() {
235 return;
236 }
237 match self {
238 Json::Null => out.push_str("null"),
239 Json::Bool(true) => out.push_str("true"),
240 Json::Bool(false) => out.push_str("false"),
241 Json::Num(n) => {
242 if n.is_finite() {
243 if n.fract() == 0.0 && n.abs() < 9.0e15 {
244 let _ = write!(out, "{}", *n as i64);
245 } else {
246 let _ = write!(out, "{}", n);
247 }
248 } else {
249 out.push_str("null");
250 }
251 }
252 Json::Str(s) => write_escaped(out, s),
253 Json::Arr(items) => {
254 if items.is_empty() {
255 out.push_str("[]");
256 return;
257 }
258 out.push('[');
259 for (i, item) in items.iter().enumerate() {
260 if out.exhausted() {
261 return;
262 }
263 if i > 0 {
264 out.push(',');
265 }
266 newline_indent(out, indent, depth + 1);
267 item.write(out, indent, depth + 1);
268 }
269 newline_indent(out, indent, depth);
270 out.push(']');
271 }
272 Json::Obj(map) => {
273 if map.is_empty() {
274 out.push_str("{}");
275 return;
276 }
277 out.push('{');
278 for (i, (k, v)) in map.iter().enumerate() {
279 if out.exhausted() {
280 return;
281 }
282 if i > 0 {
283 out.push(',');
284 }
285 newline_indent(out, indent, depth + 1);
286 write_escaped(out, k);
287 out.push(':');
288 if indent.is_some() {
289 out.push(' ');
290 }
291 v.write(out, indent, depth + 1);
292 }
293 newline_indent(out, indent, depth);
294 out.push('}');
295 }
296 }
297 }
298}
299
300fn newline_indent(out: &mut impl Output, indent: Option<usize>, depth: usize) {
301 if let Some(width) = indent {
302 out.push('\n');
303 for _ in 0..(width * depth) {
304 out.push(' ');
305 }
306 }
307}
308
309fn write_escaped(out: &mut impl Output, s: &str) {
310 out.push('"');
311 for ch in s.chars() {
312 if out.exhausted() {
313 return;
314 }
315 match ch {
316 '"' => out.push_str("\\\""),
317 '\\' => out.push_str("\\\\"),
318 '\n' => out.push_str("\\n"),
319 '\r' => out.push_str("\\r"),
320 '\t' => out.push_str("\\t"),
321 '\u{08}' => out.push_str("\\b"),
322 '\u{0c}' => out.push_str("\\f"),
323 c if (c as u32) < 0x20 => {
324 let _ = write!(out, "\\u{:04x}", c as u32);
325 }
326 c => out.push(c),
327 }
328 }
329 out.push('"');
330}
331
332#[derive(Debug)]
338pub struct ParseError {
339 pub message: String,
341 pub offset: usize,
343}
344
345impl std::fmt::Display for ParseError {
346 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
347 write!(f, "invalid JSON at byte {}: {}", self.offset, self.message)
348 }
349}
350
351impl std::error::Error for ParseError {}
352
353pub fn parse(input: &str) -> Result<Json, ParseError> {
367 let bytes = input.as_bytes();
368 let mut p = Parser {
369 bytes,
370 pos: 0,
371 depth: 0,
372 };
373 p.skip_ws();
374 let value = p.value()?;
375 p.skip_ws();
376 if p.pos != bytes.len() {
377 return Err(p.err("trailing data after the root value"));
378 }
379 Ok(value)
380}
381
382struct Parser<'a> {
383 bytes: &'a [u8],
384 pos: usize,
385 depth: usize,
386}
387
388impl<'a> Parser<'a> {
389 fn err(&self, msg: &str) -> ParseError {
390 ParseError {
391 message: msg.to_string(),
392 offset: self.pos,
393 }
394 }
395
396 fn peek(&self) -> Option<u8> {
397 self.bytes.get(self.pos).copied()
398 }
399
400 fn skip_ws(&mut self) {
401 while matches!(self.peek(), Some(b' ' | b'\t' | b'\n' | b'\r')) {
402 self.pos += 1;
403 }
404 }
405
406 fn expect(&mut self, byte: u8) -> Result<(), ParseError> {
407 if self.peek() == Some(byte) {
408 self.pos += 1;
409 Ok(())
410 } else {
411 Err(self.err(&format!("expected '{}'", byte as char)))
412 }
413 }
414
415 fn literal(&mut self, word: &str) -> Result<(), ParseError> {
416 if self.bytes[self.pos..].starts_with(word.as_bytes()) {
417 self.pos += word.len();
418 Ok(())
419 } else {
420 Err(self.err(&format!("expected '{word}'")))
421 }
422 }
423
424 fn value(&mut self) -> Result<Json, ParseError> {
425 match self.peek() {
426 None => Err(self.err("unexpected end of input")),
427 Some(b'n') => {
428 self.literal("null")?;
429 Ok(Json::Null)
430 }
431 Some(b't') => {
432 self.literal("true")?;
433 Ok(Json::Bool(true))
434 }
435 Some(b'f') => {
436 self.literal("false")?;
437 Ok(Json::Bool(false))
438 }
439 Some(b'"') => Ok(Json::Str(self.string()?)),
440 Some(b'[' | b'{') => {
441 if self.depth >= 128 {
442 return Err(self.err("more than 128 nested containers"));
443 }
444 self.depth += 1;
445 let result = if self.peek() == Some(b'[') {
446 self.array()
447 } else {
448 self.object()
449 };
450 self.depth -= 1;
451 result
452 }
453 Some(_) => self.number(),
454 }
455 }
456
457 fn array(&mut self) -> Result<Json, ParseError> {
458 self.expect(b'[')?;
459 let mut items = Vec::new();
460 self.skip_ws();
461 if self.peek() == Some(b']') {
462 self.pos += 1;
463 return Ok(Json::Arr(items));
464 }
465 loop {
466 self.skip_ws();
467 items.push(self.value()?);
468 self.skip_ws();
469 match self.peek() {
470 Some(b',') => self.pos += 1,
471 Some(b']') => {
472 self.pos += 1;
473 return Ok(Json::Arr(items));
474 }
475 _ => return Err(self.err("expected ',' or ']'")),
476 }
477 }
478 }
479
480 fn object(&mut self) -> Result<Json, ParseError> {
481 self.expect(b'{')?;
482 let mut map = BTreeMap::new();
483 self.skip_ws();
484 if self.peek() == Some(b'}') {
485 self.pos += 1;
486 return Ok(Json::Obj(map));
487 }
488 loop {
489 self.skip_ws();
490 let key = self.string()?;
491 self.skip_ws();
492 self.expect(b':')?;
493 self.skip_ws();
494 let value = self.value()?;
495 map.insert(key, value);
496 self.skip_ws();
497 match self.peek() {
498 Some(b',') => self.pos += 1,
499 Some(b'}') => {
500 self.pos += 1;
501 return Ok(Json::Obj(map));
502 }
503 _ => return Err(self.err("expected ',' or '}'")),
504 }
505 }
506 }
507
508 fn string(&mut self) -> Result<String, ParseError> {
509 self.expect(b'"')?;
510 let mut out = String::new();
511 loop {
512 let byte = self.peek().ok_or_else(|| self.err("unterminated string"))?;
513 match byte {
514 b'"' => {
515 self.pos += 1;
516 return Ok(out);
517 }
518 b'\\' => {
519 self.pos += 1;
520 let esc = self.peek().ok_or_else(|| self.err("truncated escape"))?;
521 self.pos += 1;
522 match esc {
523 b'"' => out.push('"'),
524 b'\\' => out.push('\\'),
525 b'/' => out.push('/'),
526 b'b' => out.push('\u{08}'),
527 b'f' => out.push('\u{0c}'),
528 b'n' => out.push('\n'),
529 b'r' => out.push('\r'),
530 b't' => out.push('\t'),
531 b'u' => {
532 let hi = self.hex4()?;
533 let cp = if (0xD800..0xDC00).contains(&hi) {
534 self.expect(b'\\')?;
535 self.expect(b'u')?;
536 let lo = self.hex4()?;
537 if !(0xDC00..=0xDFFF).contains(&lo) {
538 return Err(self.err("invalid low UTF-16 surrogate"));
539 }
540 0x10000
541 + ((u32::from(hi) - 0xD800) << 10)
542 + (u32::from(lo) - 0xDC00)
543 } else {
544 u32::from(hi)
545 };
546 let ch = char::from_u32(cp)
547 .ok_or_else(|| self.err("invalid Unicode scalar"))?;
548 out.push(ch);
549 }
550 _ => return Err(self.err("unknown escape sequence")),
551 }
552 }
553 0x00..=0x1F => return Err(self.err("unescaped control character in string")),
554 _ => {
555 let start = self.pos;
557 let width = utf8_width(byte);
558 self.pos = (start + width).min(self.bytes.len());
559 match std::str::from_utf8(&self.bytes[start..self.pos]) {
560 Ok(s) => out.push_str(s),
561 Err(_) => return Err(self.err("invalid UTF-8 sequence")),
562 }
563 }
564 }
565 }
566 }
567
568 fn hex4(&mut self) -> Result<u16, ParseError> {
569 if self.pos + 4 > self.bytes.len() {
570 return Err(self.err("truncated \\u escape"));
571 }
572 let slice = &self.bytes[self.pos..self.pos + 4];
573 if !slice.iter().all(u8::is_ascii_hexdigit) {
576 return Err(self.err("non-hexadecimal \\u escape"));
577 }
578 let text =
579 std::str::from_utf8(slice).map_err(|_| self.err("non-hexadecimal \\u escape"))?;
580 let value =
581 u16::from_str_radix(text, 16).map_err(|_| self.err("non-hexadecimal \\u escape"))?;
582 self.pos += 4;
583 Ok(value)
584 }
585
586 fn number(&mut self) -> Result<Json, ParseError> {
587 let start = self.pos;
588 if self.peek() == Some(b'-') {
589 self.pos += 1;
590 }
591 match self.peek() {
592 Some(b'0') => {
593 self.pos += 1;
594 if matches!(self.peek(), Some(b'0'..=b'9')) {
595 return Err(self.err("leading zero in number"));
596 }
597 }
598 Some(b'1'..=b'9') => {
599 self.digits()?;
600 }
601 _ => return Err(self.err("expected a number")),
602 }
603 if self.peek() == Some(b'.') {
604 self.pos += 1;
605 self.digits()?;
606 }
607 if matches!(self.peek(), Some(b'e' | b'E')) {
608 self.pos += 1;
609 if matches!(self.peek(), Some(b'+' | b'-')) {
610 self.pos += 1;
611 }
612 self.digits()?;
613 }
614 let text = std::str::from_utf8(&self.bytes[start..self.pos])
615 .map_err(|_| self.err("malformed number"))?;
616 let number = text.parse::<f64>().map_err(|_| ParseError {
617 message: "malformed number".into(),
618 offset: start,
619 })?;
620 if !number.is_finite() {
621 return Err(self.err("number is outside the finite range"));
622 }
623 Ok(Json::Num(number))
624 }
625
626 fn digits(&mut self) -> Result<(), ParseError> {
627 let start = self.pos;
628 while matches!(self.peek(), Some(b'0'..=b'9')) {
629 self.pos += 1;
630 }
631 if self.pos == start {
632 return Err(self.err("expected a decimal digit"));
633 }
634 Ok(())
635 }
636}
637
638fn utf8_width(first: u8) -> usize {
639 match first {
640 0x00..=0x7F => 1,
641 0xC0..=0xDF => 2,
642 0xE0..=0xEF => 3,
643 0xF0..=0xF7 => 4,
644 _ => 1,
645 }
646}
647
648#[cfg(test)]
649#[path = "json_tests.rs"]
650mod tests;