ferrijs_std/json/
parse.rs1use crate::utils::bytes::ObjectBytes;
5use rquickjs::{Array, Ctx, Exception, IntoJs, Null, Object, Result, Undefined, Value};
6use simd_json::{Node, StaticNode};
7
8pub fn json_parse_string<'js>(ctx: Ctx<'js>, bytes: ObjectBytes<'js>) -> Result<Value<'js>> {
9 let bytes = bytes.as_bytes(&ctx)?;
10 json_parse(&ctx, bytes)
11}
12
13pub fn json_parse<'js, T: Into<Vec<u8>>>(ctx: &Ctx<'js>, json: T) -> Result<Value<'js>> {
14 let mut json: Vec<u8> = json.into();
15 let tape = match simd_json::to_tape(&mut json) {
16 Ok(tape) => tape,
17 Err(err) => {
18 if err.character() == Some('u') {
24 if let Ok(value) = ctx.json_parse(json.as_slice()) {
25 return Ok(value);
26 }
27 }
28 let mut itoa = itoa::Buffer::new();
29 let mut error_msg = String::with_capacity(256);
30 let json_length = json.len();
31 if json_length < 128 {
32 error_msg.reserve(json_length);
33 error_msg.push('\"');
34 error_msg.push_str(&std::string::String::from_utf8_lossy(&json));
35 error_msg.push_str("\" ");
36 }
37
38 error_msg.push_str("not valid JSON at index ");
39 error_msg.push_str(itoa.format(err.index()));
40 if let Some(char) = err.character() {
41 error_msg.push_str(" ('");
42 error_msg.push(char);
43 error_msg.push_str("')");
44 }
45 return Err(Exception::throw_syntax(ctx, &error_msg));
46 },
47 };
48 let tape = tape.0;
49
50 if let Some(first) = tape.first() {
51 return match first {
52 Node::String(value) => value.into_js(ctx),
53 Node::Static(node) => static_node_to_value(ctx, *node),
54 _ => parse_node(ctx, &tape, 0).map(|(value, _)| value),
55 };
56 }
57
58 Undefined.into_js(ctx)
59}
60
61#[inline(always)]
62fn static_node_to_value<'js>(ctx: &Ctx<'js>, node: StaticNode) -> Result<Value<'js>> {
63 match node {
64 StaticNode::I64(value) => value.into_js(ctx),
65 StaticNode::U64(value) => value.into_js(ctx),
66 StaticNode::F64(value) => value.into_js(ctx),
67 StaticNode::Bool(value) => value.into_js(ctx),
68 StaticNode::Null => Null.into_js(ctx),
69 }
70}
71
72fn parse_node<'js>(ctx: &Ctx<'js>, tape: &[Node], index: usize) -> Result<(Value<'js>, usize)> {
73 match tape[index] {
74 Node::String(value) => Ok((value.into_js(ctx)?, index + 1)),
75 Node::Static(node) => Ok((static_node_to_value(ctx, node)?, index + 1)),
76 Node::Object { len, .. } => {
77 let js_object = Object::new(ctx.clone())?;
78 let mut current_index = index + 1;
79
80 for _ in 0..len {
81 if let Node::String(key) = tape[current_index] {
82 current_index += 1;
83 let (value, new_index) = parse_node(ctx, tape, current_index)?;
84 current_index = new_index;
85 js_object.set(key, value)?;
86 }
87 }
88
89 Ok((js_object.into_value(), current_index))
90 },
91 Node::Array { len, .. } => {
92 let js_array = Array::new(ctx.clone())?;
93 let mut current_index = index + 1;
94
95 for i in 0..len {
96 let (value, new_index) = parse_node(ctx, tape, current_index)?;
97 current_index = new_index;
98 js_array.set(i, value)?;
99 }
100
101 Ok((js_array.into_value(), current_index))
102 },
103 }
104}