shapely_json/
to_json.rs

1use shapely::{Shape, ShapeDesc};
2use std::io::{self, Write};
3
4/// Serializes any Shapely type to JSON
5pub fn to_json<W: Write>(
6    data: *mut u8,
7    shape_desc: ShapeDesc,
8    writer: &mut W,
9    indent: bool,
10) -> io::Result<()> {
11    use shapely::{Innards, Scalar};
12
13    fn serialize_value<W: Write>(
14        data: *const u8,
15        shape: Shape,
16        writer: &mut W,
17        indent: bool,
18        level: usize,
19    ) -> io::Result<()> {
20        match &shape.innards {
21            Innards::Scalar(scalar) => match scalar {
22                Scalar::String => {
23                    let s = unsafe { &*(data as *const String) };
24                    write!(writer, "\"{}\"", s.replace('"', "\\\""))
25                }
26                Scalar::Bytes => Err(io::Error::new(
27                    io::ErrorKind::InvalidData,
28                    "cowardly refusing to inject binary data straight into your JSON",
29                )),
30                Scalar::I8 => {
31                    let value = unsafe { *(data as *const i8) };
32                    write!(writer, "{value}")
33                }
34                Scalar::I16 => {
35                    let value = unsafe { *(data as *const i16) };
36                    write!(writer, "{value}")
37                }
38                Scalar::I32 => {
39                    let value = unsafe { *(data as *const i32) };
40                    write!(writer, "{value}")
41                }
42                Scalar::I64 => {
43                    let value = unsafe { *(data as *const i64) };
44                    write!(writer, "{value}")
45                }
46                Scalar::I128 => {
47                    let value = unsafe { *(data as *const i128) };
48                    write!(writer, "{value}")
49                }
50                Scalar::U8 => {
51                    let value = unsafe { *data };
52                    write!(writer, "{value}")
53                }
54                Scalar::U16 => {
55                    let value = unsafe { *(data as *const u16) };
56                    write!(writer, "{value}")
57                }
58                Scalar::U32 => {
59                    let value = unsafe { *(data as *const u32) };
60                    write!(writer, "{value}")
61                }
62                Scalar::U64 => {
63                    let value = unsafe { *(data as *const u64) };
64                    write!(writer, "{value}")
65                }
66                Scalar::U128 => {
67                    let value = unsafe { *(data as *const u128) };
68                    write!(writer, "{value}")
69                }
70                Scalar::F32 => {
71                    let value = unsafe { *(data as *const f32) };
72                    write!(writer, "{value}")
73                }
74                Scalar::F64 => {
75                    let value = unsafe { *(data as *const f64) };
76                    write!(writer, "{value}")
77                }
78                Scalar::Boolean => {
79                    let value = unsafe { *(data as *const bool) };
80                    write!(writer, "{}", if value { "true" } else { "false" })
81                }
82                Scalar::Nothing => {
83                    write!(writer, "null")
84                }
85                _ => Err(io::Error::new(
86                    io::ErrorKind::InvalidData,
87                    "unsupported scalar type encountered",
88                )),
89            },
90            Innards::Struct { fields } => {
91                write!(writer, "{{")?;
92                if indent {
93                    writeln!(writer)?;
94                }
95                for (i, field) in fields.iter().enumerate() {
96                    if indent {
97                        write!(writer, "{:indent$}", "", indent = (level + 1) * 2)?;
98                    }
99                    write!(writer, "\"{}\":", field.name)?;
100                    if indent {
101                        write!(writer, " ")?;
102                    }
103                    let field_data = unsafe { data.add(field.offset) };
104                    serialize_value(field_data, field.shape.get(), writer, indent, level + 1)?;
105                    if i < fields.len() - 1 {
106                        write!(writer, ",")?;
107                    }
108                    if indent {
109                        writeln!(writer)?;
110                    }
111                }
112                if indent {
113                    write!(writer, "{:indent$}", "", indent = level * 2)?;
114                }
115                write!(writer, "}}")
116            }
117            Innards::List { vtable, item_shape } => {
118                write!(writer, "[")?;
119                if indent {
120                    writeln!(writer)?;
121                }
122
123                unsafe {
124                    let len = (vtable.len)(data);
125
126                    for i in 0..len {
127                        if indent {
128                            write!(writer, "{:indent$}", "", indent = (level + 1) * 2)?;
129                        }
130
131                        let item_ptr = (vtable.get_item_ptr)(data, i);
132                        serialize_value(item_ptr, item_shape.get(), writer, indent, level + 1)?;
133
134                        if i < len - 1 {
135                            write!(writer, ",")?;
136                        }
137                        if indent {
138                            writeln!(writer)?;
139                        }
140                    }
141
142                    if indent && len > 0 {
143                        write!(writer, "{:indent$}", "", indent = level * 2)?;
144                    }
145                }
146                write!(writer, "]")
147            }
148            Innards::Map {
149                vtable,
150                value_shape,
151            } => {
152                write!(writer, "{{")?;
153                if indent {
154                    writeln!(writer)?;
155                }
156
157                unsafe {
158                    // Get an iterator over the HashMap
159                    let iter_ptr = (vtable.iter)(data);
160
161                    // Keep track of whether we need to write a comma
162                    let mut first = true;
163
164                    // Iterate over the key-value pairs
165                    while let Some((key_ptr, value_ptr)) = (vtable.iter_vtable.next)(iter_ptr) {
166                        if !first {
167                            write!(writer, ",")?;
168                            if indent {
169                                writeln!(writer)?;
170                            }
171                        }
172                        first = false;
173
174                        if indent {
175                            write!(writer, "{:indent$}", "", indent = (level + 1) * 2)?;
176                        }
177
178                        // Serialize the key as a string
179                        let key = &(*key_ptr);
180                        write!(writer, "\"{}\":", key.replace('"', "\\\""))?;
181
182                        if indent {
183                            write!(writer, " ")?;
184                        }
185
186                        // Serialize the value
187                        serialize_value(value_ptr, value_shape.get(), writer, indent, level + 1)?;
188                    }
189
190                    // Deallocate the iterator
191                    (vtable.iter_vtable.dealloc)(iter_ptr);
192
193                    if !first && indent {
194                        writeln!(writer)?;
195                        write!(writer, "{:indent$}", "", indent = level * 2)?;
196                    }
197                }
198
199                write!(writer, "}}")
200            }
201            // Add support for other shapes (Array, Transparent) as needed
202            _ => write!(writer, "null"),
203        }
204    }
205
206    serialize_value(data, shape_desc.get(), writer, indent, 0)
207}
208
209/// Serializes any Shapely type to JSON and returns it as a String
210pub fn to_json_string(data: *mut u8, shape_desc: ShapeDesc, indent: bool) -> String {
211    let mut buffer = Vec::new();
212    to_json(data, shape_desc, &mut buffer, indent).unwrap();
213    String::from_utf8(buffer).unwrap()
214}