1use std::fmt;
5
6#[derive(Clone, Debug, PartialEq)]
8pub enum JsonValue {
9 Null,
10 Bool(bool),
11 Integer(i64),
12 Number(f64),
13 Decimal(String),
14 String(String),
15 Array(Vec<JsonValue>),
16 Object(Vec<(String, JsonValue)>),
17}
18
19impl JsonValue {
20 pub fn as_str(&self) -> Option<&str> {
22 match self {
23 JsonValue::String(s) => Some(s.as_str()),
24 _ => None,
25 }
26 }
27
28 pub fn as_f64(&self) -> Option<f64> {
30 match self {
31 JsonValue::Number(n) => Some(*n),
32 JsonValue::Integer(n) => Some(*n as f64),
33 JsonValue::Decimal(n) => n.parse::<f64>().ok(),
34 _ => None,
35 }
36 }
37
38 pub fn as_i64(&self) -> Option<i64> {
40 match self {
41 JsonValue::Integer(n) => Some(*n),
42 _ => None,
43 }
44 }
45
46 pub fn as_bool(&self) -> Option<bool> {
48 match self {
49 JsonValue::Bool(b) => Some(*b),
50 _ => None,
51 }
52 }
53
54 pub fn as_array(&self) -> Option<&[JsonValue]> {
56 match self {
57 JsonValue::Array(items) => Some(items.as_slice()),
58 _ => None,
59 }
60 }
61
62 pub fn as_array_mut(&mut self) -> Option<&mut Vec<JsonValue>> {
64 match self {
65 JsonValue::Array(items) => Some(items),
66 _ => None,
67 }
68 }
69
70 pub fn as_object(&self) -> Option<&[(String, JsonValue)]> {
72 match self {
73 JsonValue::Object(entries) => Some(entries.as_slice()),
74 _ => None,
75 }
76 }
77
78 pub fn as_object_mut(&mut self) -> Option<&mut Vec<(String, JsonValue)>> {
80 match self {
81 JsonValue::Object(entries) => Some(entries),
82 _ => None,
83 }
84 }
85
86 pub fn get(&self, key: &str) -> Option<&JsonValue> {
88 if let JsonValue::Object(entries) = self {
89 for (k, v) in entries {
90 if k == key {
91 return Some(v);
92 }
93 }
94 }
95 None
96 }
97
98 pub fn get_mut(&mut self, key: &str) -> Option<&mut JsonValue> {
100 if let JsonValue::Object(entries) = self {
101 for (k, v) in entries.iter_mut() {
102 if k == key {
103 return Some(v);
104 }
105 }
106 }
107 None
108 }
109
110 pub fn object(entries: Vec<(String, JsonValue)>) -> JsonValue {
112 JsonValue::Object(entries)
113 }
114
115 pub fn array(items: Vec<JsonValue>) -> JsonValue {
117 JsonValue::Array(items)
118 }
119
120 #[deprecated(
132 note = "Use crate::serde_json::Value::to_string_compact for boundary emission; see ADR 0010 / issue #177"
133 )]
134 pub fn to_json_string(&self) -> String {
135 let mut out = String::new();
136 self.write_json(&mut out);
137 out
138 }
139
140 fn write_json(&self, out: &mut String) {
141 match self {
142 JsonValue::Null => out.push_str("null"),
143 JsonValue::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
144 JsonValue::Integer(n) => out.push_str(&format!("{n}")),
145 JsonValue::Number(n) => {
146 if n.fract() == 0.0 {
147 out.push_str(&format!("{}", *n as i64));
148 } else {
149 out.push_str(&format!("{}", n));
150 }
151 }
152 JsonValue::Decimal(n) => out.push_str(n),
153 JsonValue::String(s) => {
154 out.push('"');
155 for ch in s.chars() {
156 match ch {
157 '"' => out.push_str("\\\""),
158 '\\' => out.push_str("\\\\"),
159 '\n' => out.push_str("\\n"),
160 '\r' => out.push_str("\\r"),
161 '\t' => out.push_str("\\t"),
162 c if c.is_control() => {
163 out.push_str(&format!("\\u{:04x}", c as u32));
164 }
165 c => out.push(c),
166 }
167 }
168 out.push('"');
169 }
170 JsonValue::Array(items) => {
171 out.push('[');
172 for (idx, item) in items.iter().enumerate() {
173 if idx > 0 {
174 out.push(',');
175 }
176 item.write_json(out);
177 }
178 out.push(']');
179 }
180 JsonValue::Object(entries) => {
181 out.push('{');
182 for (idx, (key, value)) in entries.iter().enumerate() {
183 if idx > 0 {
184 out.push(',');
185 }
186 JsonValue::String(key.clone()).write_json(out);
187 out.push(':');
188 value.write_json(out);
189 }
190 out.push('}');
191 }
192 }
193 }
194}
195
196impl From<&str> for JsonValue {
197 fn from(value: &str) -> JsonValue {
198 JsonValue::String(value.to_string())
199 }
200}
201
202impl From<String> for JsonValue {
203 fn from(value: String) -> JsonValue {
204 JsonValue::String(value)
205 }
206}
207
208impl From<bool> for JsonValue {
209 fn from(value: bool) -> JsonValue {
210 JsonValue::Bool(value)
211 }
212}
213
214impl From<f64> for JsonValue {
215 fn from(value: f64) -> JsonValue {
216 JsonValue::Number(value)
217 }
218}
219
220impl From<i64> for JsonValue {
221 fn from(value: i64) -> JsonValue {
222 JsonValue::Integer(value)
223 }
224}
225
226impl From<usize> for JsonValue {
227 fn from(value: usize) -> JsonValue {
228 i64::try_from(value)
229 .map(JsonValue::Integer)
230 .unwrap_or_else(|_| JsonValue::Number(value as f64))
231 }
232}
233
234impl fmt::Display for JsonValue {
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 #[allow(deprecated)]
241 let s = self.to_json_string();
242 write!(f, "{s}")
243 }
244}
245
246pub struct JsonParser<'a> {
248 input: &'a [u8],
249 pos: usize,
250 depth: u32,
254}
255
256pub const JSON_MAX_NESTING_DEPTH: u32 = 128;
260
261impl<'a> JsonParser<'a> {
262 pub fn new(input: &'a str) -> Self {
264 Self {
265 input: input.as_bytes(),
266 pos: 0,
267 depth: 0,
268 }
269 }
270
271 fn push_depth(&mut self) -> Result<(), String> {
273 if self.depth >= JSON_MAX_NESTING_DEPTH {
274 return Err(format!(
275 "json nesting depth exceeds {JSON_MAX_NESTING_DEPTH}"
276 ));
277 }
278 self.depth += 1;
279 Ok(())
280 }
281
282 pub fn parse_value(&mut self) -> Result<JsonValue, String> {
284 self.skip_whitespace();
285 if self.eof() {
286 return Err("unexpected end of input".to_string());
287 }
288 let ch = self.current_char();
289 match ch {
290 b'n' => self.parse_null(),
291 b't' | b'f' => self.parse_bool(),
292 b'-' | b'0'..=b'9' => self.parse_number(),
293 b'"' => self.parse_string().map(JsonValue::String),
294 b'[' => {
295 self.push_depth()?;
296 let value = self.parse_array();
297 self.depth -= 1;
298 value
299 }
300 b'{' => {
301 self.push_depth()?;
302 let value = self.parse_object();
303 self.depth -= 1;
304 value
305 }
306 _ => Err(format!("unexpected character '{}'", ch as char)),
307 }
308 }
309
310 fn parse_null(&mut self) -> Result<JsonValue, String> {
311 self.expect_bytes(b"null")?;
312 Ok(JsonValue::Null)
313 }
314
315 fn parse_bool(&mut self) -> Result<JsonValue, String> {
316 if self.matches_bytes(b"true") {
317 self.pos += 4;
318 Ok(JsonValue::Bool(true))
319 } else if self.matches_bytes(b"false") {
320 self.pos += 5;
321 Ok(JsonValue::Bool(false))
322 } else {
323 Err("invalid boolean literal".to_string())
324 }
325 }
326
327 fn parse_number(&mut self) -> Result<JsonValue, String> {
328 let start = self.pos;
329 if self.current_char() == b'-' {
330 self.pos += 1;
331 }
332 if self.eof() {
333 return Err("invalid number literal".to_string());
334 }
335
336 match self.current_char() {
337 b'0' => {
338 self.pos += 1;
339 }
340 b'1'..=b'9' => {
341 self.pos += 1;
342 while !self.eof() && self.current_char().is_ascii_digit() {
343 self.pos += 1;
344 }
345 }
346 _ => return Err("invalid number literal".to_string()),
347 }
348
349 let mut is_float = false;
350
351 if !self.eof() && self.current_char() == b'.' {
352 is_float = true;
353 self.pos += 1;
354 if self.eof() || !self.current_char().is_ascii_digit() {
355 return Err("invalid number literal".to_string());
356 }
357 while !self.eof() && self.current_char().is_ascii_digit() {
358 self.pos += 1;
359 }
360 }
361
362 if !self.eof() && (self.current_char() == b'e' || self.current_char() == b'E') {
363 is_float = true;
364 self.pos += 1;
365 if !self.eof() && (self.current_char() == b'+' || self.current_char() == b'-') {
366 self.pos += 1;
367 }
368 if self.eof() || !self.current_char().is_ascii_digit() {
369 return Err("invalid number literal".to_string());
370 }
371 while !self.eof() && self.current_char().is_ascii_digit() {
372 self.pos += 1;
373 }
374 }
375
376 let slice = &self.input[start..self.pos];
377 let s = std::str::from_utf8(slice).map_err(|_| "invalid UTF-8 in number".to_string())?;
378 if !is_float {
379 if let Ok(value) = s.parse::<i64>() {
380 return Ok(JsonValue::Integer(value));
381 }
382 return Ok(JsonValue::Decimal(s.to_string()));
383 }
384 if should_preserve_decimal_text(s) {
385 return Ok(JsonValue::Decimal(s.to_string()));
386 }
387 let value = s
388 .parse::<f64>()
389 .map_err(|_| "failed to parse number".to_string())?;
390 if !value.is_finite() {
391 return Ok(JsonValue::Decimal(s.to_string()));
392 }
393 Ok(JsonValue::Number(value))
394 }
395
396 fn parse_string(&mut self) -> Result<String, String> {
397 self.expect_char(b'"')?;
398 let mut result = String::new();
399 while !self.eof() {
400 let ch = self.current_char();
401 match ch {
402 b'"' => {
403 self.pos += 1;
404 return Ok(result);
405 }
406 b'\\' => {
407 self.pos += 1;
408 if self.eof() {
409 return Err("unexpected end of input in escape".to_string());
410 }
411 let esc = self.current_char();
412 self.pos += 1;
413 match esc {
414 b'"' => result.push('"'),
415 b'\\' => result.push('\\'),
416 b'/' => result.push('/'),
417 b'b' => result.push('\x08'),
418 b'f' => result.push('\x0c'),
419 b'n' => result.push('\n'),
420 b'r' => result.push('\r'),
421 b't' => result.push('\t'),
422 b'u' => {
423 let code = self.parse_unicode_escape()?;
429 if (0xD800..=0xDBFF).contains(&code) {
430 if self.pos + 2 > self.input.len()
433 || self.input[self.pos] != b'\\'
434 || self.input[self.pos + 1] != b'u'
435 {
436 return Err(
437 "expected low surrogate after high surrogate".to_string()
438 );
439 }
440 self.pos += 2;
441 let low = self.parse_unicode_escape()?;
442 if !(0xDC00..=0xDFFF).contains(&low) {
443 return Err("invalid low surrogate".to_string());
444 }
445 let scalar = 0x10000 + (((code - 0xD800) << 10) | (low - 0xDC00));
446 if let Some(chr) = char::from_u32(scalar) {
447 result.push(chr);
448 } else {
449 return Err("invalid unicode escape".to_string());
450 }
451 } else if (0xDC00..=0xDFFF).contains(&code) {
452 return Err(
453 "unexpected low surrogate without preceding high surrogate"
454 .to_string(),
455 );
456 } else if let Some(chr) = char::from_u32(code) {
457 result.push(chr);
458 } else {
459 return Err("invalid unicode escape".to_string());
460 }
461 }
462 _ => return Err("invalid escape sequence".to_string()),
463 }
464 }
465 _ if ch < 0x80 => {
466 self.pos += 1;
468 result.push(ch as char);
469 }
470 _ => {
471 let next = std::str::from_utf8(&self.input[self.pos..])
480 .map_err(|_| "invalid utf-8 in string body".to_string())?
481 .chars()
482 .next()
483 .ok_or_else(|| "unterminated string literal".to_string())?;
484 self.pos += next.len_utf8();
485 result.push(next);
486 }
487 }
488 }
489 Err("unterminated string literal".to_string())
490 }
491
492 fn parse_unicode_escape(&mut self) -> Result<u32, String> {
493 if self.pos + 4 > self.input.len() {
494 return Err("invalid unicode escape".to_string());
495 }
496 let mut value = 0u32;
497 for _ in 0..4 {
498 let ch = self.current_char();
499 self.pos += 1;
500 value <<= 4;
501 value |= match ch {
502 b'0'..=b'9' => (ch - b'0') as u32,
503 b'a'..=b'f' => (ch - b'a' + 10) as u32,
504 b'A'..=b'F' => (ch - b'A' + 10) as u32,
505 _ => return Err("invalid unicode escape".to_string()),
506 };
507 }
508 Ok(value)
509 }
510
511 fn parse_array(&mut self) -> Result<JsonValue, String> {
512 self.expect_char(b'[')?;
513 let mut items = Vec::new();
514 self.skip_whitespace();
515 if self.peek_char() == Some(b']') {
516 self.pos += 1;
517 return Ok(JsonValue::Array(items));
518 }
519 loop {
520 let value = self.parse_value()?;
521 items.push(value);
522 self.skip_whitespace();
523 match self.peek_char() {
524 Some(b',') => {
525 self.pos += 1;
526 }
527 Some(b']') => {
528 self.pos += 1;
529 break;
530 }
531 _ => return Err("expected ',' or ']' in array".to_string()),
532 }
533 }
534 Ok(JsonValue::Array(items))
535 }
536
537 fn parse_object(&mut self) -> Result<JsonValue, String> {
538 self.expect_char(b'{')?;
539 let mut entries = Vec::new();
540 self.skip_whitespace();
541 if self.peek_char() == Some(b'}') {
542 self.pos += 1;
543 return Ok(JsonValue::Object(entries));
544 }
545 loop {
546 self.skip_whitespace();
547 let key = self.parse_string()?;
548 self.skip_whitespace();
549 self.expect_char(b':')?;
550 let value = self.parse_value()?;
551 entries.push((key, value));
552 self.skip_whitespace();
553 match self.peek_char() {
554 Some(b',') => {
555 self.pos += 1;
556 }
557 Some(b'}') => {
558 self.pos += 1;
559 break;
560 }
561 _ => return Err("expected ',' or '}' in object".to_string()),
562 }
563 }
564 Ok(JsonValue::Object(entries))
565 }
566
567 fn skip_whitespace(&mut self) {
568 while !self.eof() {
569 match self.current_char() {
570 b' ' | b'\n' | b'\r' | b'\t' => self.pos += 1,
571 _ => break,
572 }
573 }
574 }
575
576 fn expect_bytes(&mut self, expected: &[u8]) -> Result<(), String> {
577 if self.remaining().starts_with(expected) {
578 self.pos += expected.len();
579 Ok(())
580 } else {
581 Err("unexpected token".to_string())
582 }
583 }
584
585 fn matches_bytes(&self, expected: &[u8]) -> bool {
586 self.remaining().starts_with(expected)
587 }
588
589 fn expect_char(&mut self, expected: u8) -> Result<(), String> {
590 if self.peek_char() == Some(expected) {
591 self.pos += 1;
592 Ok(())
593 } else {
594 Err(format!("expected '{}'", expected as char))
595 }
596 }
597
598 fn peek_char(&self) -> Option<u8> {
599 if self.pos >= self.input.len() {
600 None
601 } else {
602 Some(self.input[self.pos])
603 }
604 }
605
606 fn current_char(&self) -> u8 {
607 self.input[self.pos]
608 }
609
610 fn remaining(&self) -> &[u8] {
611 &self.input[self.pos..]
612 }
613
614 fn eof(&self) -> bool {
615 self.pos >= self.input.len()
616 }
617}
618
619fn should_preserve_decimal_text(input: &str) -> bool {
620 let significant_digits = input.bytes().filter(u8::is_ascii_digit).count();
621 significant_digits > 15
622}
623
624pub fn parse_json(input: &str) -> Result<JsonValue, String> {
626 let mut parser = JsonParser::new(input);
627 let value = parser.parse_value()?;
628 parser.skip_whitespace();
629 if parser.eof() {
630 Ok(value)
631 } else {
632 Err("unexpected trailing data".to_string())
633 }
634}
635
636#[cfg(test)]
637mod tests {
638 use super::*;
639 use proptest::prelude::*;
640
641 #[test]
642 fn parse_simple_object() {
643 let json = r#"{"name":"reddb","active":true,"count":3}"#;
644 let value = parse_json(json).unwrap();
645 let obj = value.as_object().unwrap();
646 assert_eq!(obj.len(), 3);
647 }
648
649 #[test]
650 fn parse_nested_array() {
651 let json = r#"{"items":[1,2,{"flag":false}]}"#;
652 let value = parse_json(json).unwrap();
653 assert!(value.get("items").unwrap().as_array().is_some());
654 }
655
656 #[test]
657 fn stringify_roundtrip() {
658 let json = r#"{"message":"hello","value":42}"#;
659 let value = parse_json(json).unwrap();
660 #[allow(deprecated)]
661 let output = value.to_json_string();
662 assert!(output.contains("hello"));
663 }
664
665 #[test]
666 fn constructors_accessors_mutators_and_display_cover_all_shapes() {
667 let mut value = JsonValue::object(vec![
668 ("name".to_string(), JsonValue::from("reddb")),
669 ("active".to_string(), JsonValue::from(true)),
670 ("count".to_string(), JsonValue::from(3usize)),
671 ("ratio".to_string(), JsonValue::from(1.5f64)),
672 (
673 "items".to_string(),
674 JsonValue::array(vec![JsonValue::Null, JsonValue::from(2i64)]),
675 ),
676 ]);
677
678 assert_eq!(value.get("name").and_then(JsonValue::as_str), Some("reddb"));
679 assert_eq!(value.get("active").and_then(JsonValue::as_bool), Some(true));
680 assert_eq!(value.get("count").and_then(JsonValue::as_f64), Some(3.0));
681 assert_eq!(
682 value
683 .get("items")
684 .and_then(JsonValue::as_array)
685 .map(<[_]>::len),
686 Some(2)
687 );
688 assert_eq!(value.as_object().map(<[_]>::len), Some(5));
689 assert!(JsonValue::Null.as_str().is_none());
690 assert!(JsonValue::Null.as_bool().is_none());
691 assert!(JsonValue::Null.as_array().is_none());
692 assert!(JsonValue::Null.as_object().is_none());
693
694 value
695 .get_mut("count")
696 .map(|slot| *slot = JsonValue::from(4usize))
697 .unwrap();
698 assert_eq!(value.get("count").and_then(JsonValue::as_f64), Some(4.0));
699
700 value
701 .as_object_mut()
702 .unwrap()
703 .push(("extra".to_string(), JsonValue::from("ok".to_string())));
704 let mut array = JsonValue::array(vec![JsonValue::from(1usize)]);
705 array.as_array_mut().unwrap().push(JsonValue::from(2usize));
706 assert_eq!(array.as_array().unwrap().len(), 2);
707
708 #[allow(deprecated)]
709 let encoded =
710 JsonValue::String("quote\" slash\\ tab\t ctrl\x01".to_string()).to_json_string();
711 assert!(encoded.contains("\\\""));
712 assert!(encoded.contains("\\\\"));
713 assert!(encoded.contains("\\t"));
714 assert!(encoded.contains("\\u0001"));
715 assert_eq!(format!("{}", JsonValue::Bool(false)), "false");
716 }
717
718 #[test]
719 fn parser_accepts_scalars_escapes_empty_containers_and_trailing_space() {
720 assert_eq!(parse_json(" null \n").unwrap(), JsonValue::Null);
721 assert_eq!(parse_json("true").unwrap(), JsonValue::Bool(true));
722 assert_eq!(parse_json("false").unwrap(), JsonValue::Bool(false));
723 assert_eq!(parse_json("-12.5e+2").unwrap(), JsonValue::Number(-1250.0));
724 assert_eq!(
725 parse_json("3.14159265358979323846").unwrap(),
726 JsonValue::Decimal("3.14159265358979323846".to_string())
727 );
728 assert_eq!(
729 parse_json("18446744073709551616").unwrap(),
730 JsonValue::Decimal("18446744073709551616".to_string())
731 );
732 assert_eq!(parse_json("[]").unwrap(), JsonValue::Array(Vec::new()));
733 assert_eq!(parse_json("{}").unwrap(), JsonValue::Object(Vec::new()));
734
735 let escaped = parse_json(r#""\"\\\/\b\f\n\r\t\u00e9\uD83D\uDCA9""#).unwrap();
736 let text = escaped.as_str().unwrap();
737 assert!(text.contains('"'));
738 assert!(text.contains('\\'));
739 assert!(text.contains('/'));
740 assert!(text.contains('\x08'));
741 assert!(text.contains('\x0c'));
742 assert!(text.contains('\n'));
743 assert!(text.contains('\r'));
744 assert!(text.contains('\t'));
745 assert!(text.chars().any(|ch| ch as u32 == 0x00E9));
746 assert_eq!(text.chars().last(), char::from_u32(0x1F4A9));
747 }
748
749 #[test]
750 fn parser_rejects_invalid_tokens_numbers_strings_and_separators() {
751 let cases = [
752 "",
753 "truth",
754 "nul",
755 "-",
756 "01",
757 "1.",
758 "1e",
759 r#""unterminated"#,
760 r#""\x""#,
761 r#""\u12""#,
762 r#""\u12zz""#,
763 r#""\uD800""#,
764 r#""\uD800\u0000""#,
765 r#""\uDC00""#,
766 "[1 2]",
767 "[1,]",
768 r#"{"a" 1}"#,
769 r#"{"a":1 "b":2}"#,
770 "{} trailing",
771 "?",
772 ];
773
774 for case in cases {
775 assert!(parse_json(case).is_err(), "{case:?}");
776 }
777 }
778
779 proptest! {
780 #[test]
781 fn beyond_native_decimal_text_round_trips(
782 prefix in "[1-9][0-9]{18,36}",
783 suffix in "[0-9]{1,24}"
784 ) {
785 let input = format!("{prefix}.{suffix}");
786 let parsed = parse_json(&input).expect("generated decimal parses");
787 prop_assert_eq!(&parsed, &JsonValue::Decimal(input.clone()));
788 #[allow(deprecated)]
789 let output = parsed.to_json_string();
790 prop_assert_eq!(output, input);
791 }
792 }
793
794 #[test]
795 fn deeply_nested_json_is_rejected_instead_of_overflowing_the_stack() {
796 let bomb = "[".repeat((JSON_MAX_NESTING_DEPTH as usize) + 5);
801 let err = parse_json(&bomb).expect_err("nesting bomb must be refused");
802 assert!(err.contains("nesting depth"), "unexpected error: {err}");
803
804 let object_bomb = "{\"a\":".repeat((JSON_MAX_NESTING_DEPTH as usize) + 5);
805 let err = parse_json(&object_bomb).expect_err("object nesting bomb must be refused");
806 assert!(err.contains("nesting depth"), "unexpected error: {err}");
807 }
808
809 #[test]
810 fn nesting_just_below_the_cap_still_parses() {
811 let depth = (JSON_MAX_NESTING_DEPTH as usize) - 1;
812 let doc = format!("{}{}", "[".repeat(depth), "]".repeat(depth));
813 parse_json(&doc).expect("documents under the cap must still parse");
814 }
815}