quickjs_runtime/quickjs_utils/
json.rs1use crate::jsutils::JsError;
4use crate::quickjs_utils;
5use crate::quickjsrealmadapter::QuickJsRealmAdapter;
6use crate::quickjsvalueadapter::QuickJsValueAdapter;
7use libquickjs_sys as q;
8use std::ffi::CString;
9
10pub fn parse_q(q_ctx: &QuickJsRealmAdapter, input: &str) -> Result<QuickJsValueAdapter, JsError> {
34 unsafe { parse(q_ctx.context, input) }
35}
36
37pub unsafe fn parse(
41 context: *mut q::JSContext,
42 input: &str,
43) -> Result<QuickJsValueAdapter, JsError> {
44 let s = CString::new(input).ok().unwrap();
45 let f_n = CString::new("JSON.parse").ok().unwrap();
46
47 let len = input.len();
48
49 let val = q::JS_ParseJSON(context, s.as_ptr(), len as _, f_n.as_ptr());
50
51 let ret = QuickJsValueAdapter::new(context, val, false, true, "json::parse result");
52
53 if ret.is_exception() {
54 if let Some(ex) = QuickJsRealmAdapter::get_exception(context) {
55 Err(ex)
56 } else {
57 Err(JsError::new_str("unknown error while parsing json"))
58 }
59 } else {
60 Ok(ret)
61 }
62}
63pub fn stringify_q(
80 q_ctx: &QuickJsRealmAdapter,
81 input: &QuickJsValueAdapter,
82 opt_space: Option<QuickJsValueAdapter>,
83) -> Result<QuickJsValueAdapter, JsError> {
84 unsafe { stringify(q_ctx.context, input, opt_space) }
85}
86
87pub unsafe fn stringify(
90 context: *mut q::JSContext,
91 input: &QuickJsValueAdapter,
92 opt_space: Option<QuickJsValueAdapter>,
93) -> Result<QuickJsValueAdapter, JsError> {
94 let space_ref = match opt_space {
102 None => quickjs_utils::new_null_ref(),
103 Some(s) => s,
104 };
105
106 let val = q::JS_JSONStringify(
107 context,
108 *input.borrow_value(),
109 quickjs_utils::new_null(),
110 *space_ref.borrow_value(),
111 );
112 let ret = QuickJsValueAdapter::new(context, val, false, true, "json::stringify result");
113
114 if ret.is_exception() {
115 if let Some(ex) = QuickJsRealmAdapter::get_exception(context) {
116 Err(ex)
117 } else {
118 Err(JsError::new_str("unknown error in json::stringify"))
119 }
120 } else {
121 Ok(ret)
122 }
123}
124
125#[cfg(test)]
126pub mod tests {
127 use crate::facades::tests::init_test_rt;
128 use crate::jsutils::Script;
129 use crate::quickjs_utils::json::parse_q;
130 use crate::quickjs_utils::{get_global_q, json, objects, primitives};
131 use crate::values::JsValueFacade;
132 use std::collections::HashMap;
133
134 #[test]
135 fn test_json() {
136 let rt = init_test_rt();
137
138 log::info!("Starting json test");
139
140 rt.exe_rt_task_in_event_loop(|q_js_rt| {
141 let q_ctx = q_js_rt.get_main_realm();
142
143 let obj = objects::create_object_q(q_ctx).ok().unwrap();
144 objects::set_property_q(q_ctx, &obj, "a", &primitives::from_i32(532))
145 .ok()
146 .unwrap();
147 objects::set_property_q(q_ctx, &obj, "b", &primitives::from_bool(true))
148 .ok()
149 .unwrap();
150 objects::set_property_q(
151 q_ctx,
152 &obj,
153 "c",
154 &primitives::from_string_q(q_ctx, "abcdË").ok().unwrap(),
155 )
156 .ok()
157 .unwrap();
158 let str_res = json::stringify_q(q_ctx, &obj, None).ok().unwrap();
159
160 #[cfg(feature = "bellard")]
161 assert_eq!(str_res.get_ref_count(), 1);
162 let json = str_res.to_string().ok().unwrap();
163 assert_eq!(json, "{\"a\":532,\"b\":true,\"c\":\"abcdË\"}");
164
165 let obj2 = parse_q(q_ctx, json.as_str()).ok().unwrap();
166
167 let prop_c = objects::get_property_q(q_ctx, &obj2, "c").ok().unwrap();
168 assert_eq!("abcdË", prop_c.to_string().ok().unwrap());
169 });
170 }
171
172 #[tokio::test]
173 async fn test_json_arg() {
174 let rt = init_test_rt();
175
176 rt.eval(
178 None,
179 Script::new(
180 "myFunc.js",
181 r#"
182 function myFunction(argObj) {
183 console.log("I got an %s", typeof argObj);
184 console.log("It looks like this %s", argObj);
185 return "hello " + argObj["key"];
186 }
187 "#,
188 ),
189 )
190 .await
191 .ok()
192 .expect("myFunc failed to parse");
193
194 let mut my_json_deserable_object = HashMap::new();
196 my_json_deserable_object.insert("key", "value");
197 let json = serde_json::to_string(&my_json_deserable_object)
198 .ok()
199 .expect("serializing failed");
200
201 let func_res = rt
202 .loop_realm(None, move |_rt, realm| {
203 let js_obj = parse_q(realm, json.as_str())
206 .ok()
207 .expect("parsing json failed");
208 let global = get_global_q(realm);
211 let func_res = crate::quickjs_utils::functions::invoke_member_function_q(
213 realm,
214 &global,
215 "myFunction",
216 &[js_obj],
217 );
218 realm.to_js_value_facade(&func_res.ok().expect("func failed"))
220 })
221 .await;
222
223 let jsv = func_res.ok().expect("got err");
224 assert_eq!(jsv.stringify(), "String: hello value");
225 }
226
227 #[tokio::test]
228 async fn test_json_arg2() {
229 let rt = init_test_rt();
230
231 rt.eval(
233 None,
234 Script::new(
235 "myFunc.js",
236 r#"
237 function myFunction(argObj) {
238 console.log("I got an %s", typeof argObj);
239 console.log("It looks like this %s", argObj);
240 return "hello " + argObj["key"];
241 }
242 "#,
243 ),
244 )
245 .await
246 .ok()
247 .expect("myFunc failed to parse");
248
249 let mut my_json_deserable_object = HashMap::new();
251 my_json_deserable_object.insert("key", "value");
252 let json = serde_json::to_string(&my_json_deserable_object)
253 .ok()
254 .expect("serializing failed");
255
256 let json_js_value_facade = JsValueFacade::JsonStr { json };
257
258 let func_res = rt
259 .invoke_function(None, &[], "myFunction", vec![json_js_value_facade])
260 .await;
261
262 let jsv = func_res.ok().expect("got err");
263 assert_eq!(jsv.stringify(), "String: hello value");
264 }
265}