nanvm_lib/serializer/
to_json.rs1use crate::{
2 js::{
3 any::Any,
4 js_string::JsStringRef,
5 visitor::{to_visitor, Visitor},
6 },
7 mem::{
8 flexible_array::{header::FlexibleArrayHeader, FlexibleArray},
9 manager::Dealloc,
10 ref_::Ref,
11 },
12};
13
14use core::{
15 fmt::{self, Write},
16 result,
17};
18
19const ESCAPE_B: u8 = 0x08;
20const ESCAPE_F: u8 = 0x0C;
21
22pub trait WriteJson: Write {
23 fn write_u4_hex(&mut self, v: u16) -> fmt::Result {
24 self.write_char(b"0123456789ABCDEF"[v as usize & 0xF] as char)
25 }
26
27 fn write_js_escape(&mut self, c: u16) -> fmt::Result {
28 self.write_str("\\u")?;
29 self.write_u4_hex(c >> 12)?;
30 self.write_u4_hex(c >> 8)?;
31 self.write_u4_hex(c >> 4)?;
32 self.write_u4_hex(c)
33 }
34
35 fn write_js_string(&mut self, s: &JsStringRef<impl Dealloc>) -> fmt::Result {
37 self.write_char('"')?;
38 for &c in s.items().iter() {
39 if c < 0x80 {
40 match c as u8 {
41 b'\\' => self.write_str(r#"\\"#)?,
42 b'"' => self.write_str(r#"\""#)?,
43 ESCAPE_B => self.write_str(r#"\b"#)?,
44 ESCAPE_F => self.write_str(r#"\f"#)?,
45 b'\n' => self.write_str(r#"\n"#)?,
46 b'\r' => self.write_str(r#"\r"#)?,
47 b'\t' => self.write_str(r#"\t"#)?,
48 c if c < 0x20 => self.write_js_escape(c as u16)?,
49 c => self.write_char(c as char)?,
50 }
51 } else {
52 self.write_js_escape(c)?;
53 }
54 }
55 self.write_char('"')
56 }
57
58 fn write_list<I>(
59 &mut self,
60 open: char,
61 close: char,
62 v: Ref<FlexibleArray<I, impl FlexibleArrayHeader>, impl Dealloc>,
63 f: impl Fn(&mut Self, &I) -> fmt::Result,
64 ) -> fmt::Result {
65 let mut comma = "";
66 self.write_char(open)?;
67 for i in v.items().iter() {
68 self.write_str(comma)?;
69 f(self, i)?;
70 comma = ",";
71 }
72 self.write_char(close)
73 }
74
75 fn write_json(&mut self, any: Any<impl Dealloc>) -> fmt::Result {
76 match to_visitor(any) {
77 Visitor::Number(n) => self.write_str(n.to_string().as_str()),
79 Visitor::Null => self.write_str("null"),
80 Visitor::Bool(b) => self.write_str(if b { "true" } else { "false" }),
81 Visitor::String(s) => self.write_js_string(&s),
82 Visitor::Object(o) => self.write_list('{', '}', o, |w, (k, v)| {
83 w.write_js_string(k)?;
84 w.write_char(':')?;
85 w.write_json(v.clone())
86 }),
87 Visitor::Array(a) => self.write_list('[', ']', a, |w, i| w.write_json(i.clone())),
88 Visitor::Bigint(_) => todo!(),
89 }
90 }
91}
92
93impl<T: Write> WriteJson for T {}
94
95pub fn to_json(a: Any<impl Dealloc>) -> result::Result<String, fmt::Error> {
96 let mut s = String::default();
97 s.write_json(a)?;
98 Ok(s)
99}
100
101#[cfg(test)]
102mod test {
103 use wasm_bindgen_test::wasm_bindgen_test;
104
105 use crate::{
106 js::{any::Any, any_cast::AnyCast, js_string::new_string, new::New, null::Null},
107 mem::global::{Global, GLOBAL},
108 serializer::to_json::WriteJson,
109 };
110
111 #[test]
112 #[wasm_bindgen_test]
113 fn test() {
114 type A = Any<Global>;
115 let s = new_string(
116 GLOBAL,
117 ['a' as u16, '\\' as u16, 'b' as u16, '"' as u16, 31],
118 )
119 .to_ref();
120 let a = GLOBAL.new_js_array([
121 1.0.move_to_any(),
122 true.move_to_any(),
123 Null().move_to_any(),
124 GLOBAL.new_js_array([]),
125 GLOBAL.new_js_string([]),
126 GLOBAL.new_js_object([]),
127 GLOBAL.new_js_object([(s, 2.0.move_to_any())]),
128 ]);
129 let mut s = String::new();
130 s.write_json(a).unwrap();
131 assert_eq!(s, r#"[1,true,null,[],"",{},{"a\\b\"\u001F":2}]"#);
132 }
133}