facet_json/
serialize.rs

1use core::num::NonZero;
2use facet_core::{Def, Facet, FieldAttribute};
3use facet_reflect::Peek;
4use std::io::{self, Write};
5
6/// Serializes a value to JSON
7pub fn to_string<T: Facet>(value: &T) -> String {
8    let peek = Peek::new(value);
9    let mut output = Vec::new();
10    serialize(&peek, &mut output).unwrap();
11    String::from_utf8(output).unwrap()
12}
13
14/// Serializes a Peek instance to JSON
15pub fn peek_to_string(peek: &Peek<'_>) -> String {
16    let mut output = Vec::new();
17    serialize(peek, &mut output).unwrap();
18    String::from_utf8(output).unwrap()
19}
20
21/// Serializes a value to a writer in JSON format
22pub fn to_writer<T: Facet, W: Write>(value: &T, writer: &mut W) -> io::Result<()> {
23    let peek = Peek::new(value);
24    serialize(&peek, writer)
25}
26
27/// Serializes a Peek instance to a writer in JSON format
28pub fn peek_to_writer<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
29    serialize(peek, writer)
30}
31
32/// The core serialization function
33fn serialize<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
34    match peek.shape().def {
35        Def::Scalar(_) => serialize_scalar(peek, writer),
36        Def::Struct(_) => serialize_struct(peek, writer),
37        Def::List(_) => serialize_list(peek, writer),
38        Def::Map(_) => serialize_map(peek, writer),
39        Def::Enum(_) => serialize_enum(peek, writer),
40        Def::Option(_) => serialize_option(peek, writer),
41        _ => Err(io::Error::new(
42            io::ErrorKind::Other,
43            format!("Unsupported type: {}", peek.shape()),
44        )),
45    }
46}
47
48/// Serializes a scalar value to JSON
49fn serialize_scalar<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
50    // Handle basic scalar types
51    if peek.shape().is_type::<bool>() {
52        let value = peek.get::<bool>().unwrap();
53        write!(writer, "{}", if *value { "true" } else { "false" })
54    } else if peek.shape().is_type::<String>() {
55        let value = peek.get::<String>().unwrap();
56        write_json_string(writer, value)
57    } else if peek.shape().is_type::<&str>() {
58        let value = peek.get::<&str>().unwrap();
59        write_json_string(writer, value)
60    } else if peek.shape().is_type::<alloc::borrow::Cow<'_, str>>() {
61        let value = peek.get::<alloc::borrow::Cow<'_, str>>().unwrap();
62        write_json_string(writer, value)
63    }
64    // Integer types
65    else if peek.shape().is_type::<u8>() {
66        let value = peek.get::<u8>().unwrap();
67        write!(writer, "{}", value)
68    } else if peek.shape().is_type::<u16>() {
69        let value = peek.get::<u16>().unwrap();
70        write!(writer, "{}", value)
71    } else if peek.shape().is_type::<u32>() {
72        let value = peek.get::<u32>().unwrap();
73        write!(writer, "{}", value)
74    } else if peek.shape().is_type::<u64>() {
75        let value = peek.get::<u64>().unwrap();
76        write!(writer, "{}", value)
77    } else if peek.shape().is_type::<usize>() {
78        let value = peek.get::<usize>().unwrap();
79        write!(writer, "{}", value)
80    } else if peek.shape().is_type::<i8>() {
81        let value = peek.get::<i8>().unwrap();
82        write!(writer, "{}", value)
83    } else if peek.shape().is_type::<i16>() {
84        let value = peek.get::<i16>().unwrap();
85        write!(writer, "{}", value)
86    } else if peek.shape().is_type::<i32>() {
87        let value = peek.get::<i32>().unwrap();
88        write!(writer, "{}", value)
89    } else if peek.shape().is_type::<i64>() {
90        let value = peek.get::<i64>().unwrap();
91        write!(writer, "{}", value)
92    } else if peek.shape().is_type::<isize>() {
93        let value = peek.get::<isize>().unwrap();
94        write!(writer, "{}", value)
95    }
96    // NonZero types
97    else if peek.shape().is_type::<NonZero<u8>>() {
98        let value = peek.get::<NonZero<u8>>().unwrap();
99        write!(writer, "{}", value)
100    } else if peek.shape().is_type::<NonZero<u16>>() {
101        let value = peek.get::<NonZero<u16>>().unwrap();
102        write!(writer, "{}", value)
103    } else if peek.shape().is_type::<NonZero<u32>>() {
104        let value = peek.get::<NonZero<u32>>().unwrap();
105        write!(writer, "{}", value)
106    } else if peek.shape().is_type::<NonZero<u64>>() {
107        let value = peek.get::<NonZero<u64>>().unwrap();
108        write!(writer, "{}", value)
109    } else if peek.shape().is_type::<NonZero<usize>>() {
110        let value = peek.get::<NonZero<usize>>().unwrap();
111        write!(writer, "{}", value)
112    } else if peek.shape().is_type::<NonZero<i8>>() {
113        let value = peek.get::<NonZero<i8>>().unwrap();
114        write!(writer, "{}", value)
115    } else if peek.shape().is_type::<NonZero<i16>>() {
116        let value = peek.get::<NonZero<i16>>().unwrap();
117        write!(writer, "{}", value)
118    } else if peek.shape().is_type::<NonZero<i32>>() {
119        let value = peek.get::<NonZero<i32>>().unwrap();
120        write!(writer, "{}", value)
121    } else if peek.shape().is_type::<NonZero<i64>>() {
122        let value = peek.get::<NonZero<i64>>().unwrap();
123        write!(writer, "{}", value)
124    } else if peek.shape().is_type::<NonZero<isize>>() {
125        let value = peek.get::<NonZero<isize>>().unwrap();
126        write!(writer, "{}", value)
127    }
128    // Float types
129    else if peek.shape().is_type::<f32>() {
130        let value = peek.get::<f32>().unwrap();
131        write!(writer, "{}", value)
132    } else if peek.shape().is_type::<f64>() {
133        let value = peek.get::<f64>().unwrap();
134        write!(writer, "{}", value)
135    } else {
136        Err(io::Error::new(
137            io::ErrorKind::Other,
138            format!("Unsupported scalar type: {}", peek.shape()),
139        ))
140    }
141}
142
143/// Serializes a struct to JSON
144fn serialize_struct<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
145    let struct_peek = peek
146        .into_struct()
147        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Not a struct: {}", e)))?;
148
149    write!(writer, "{{")?;
150
151    let mut first = true;
152    for (field, field_peek) in struct_peek.fields() {
153        if !first {
154            write!(writer, ",")?;
155        }
156        first = false;
157
158        // Check for rename attribute
159        let field_name = field
160            .attributes
161            .iter()
162            .find_map(|attr| {
163                if let FieldAttribute::Rename(name) = attr {
164                    Some(*name)
165                } else {
166                    None
167                }
168            })
169            .unwrap_or(field.name);
170
171        // Write field name
172        write_json_string(writer, field_name)?;
173        write!(writer, ":")?;
174
175        // Write field value
176        serialize(&field_peek, writer)?;
177    }
178
179    write!(writer, "}}")?;
180
181    Ok(())
182}
183
184/// Serializes a list to JSON
185fn serialize_list<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
186    let list_peek = peek
187        .into_list()
188        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Not a list: {}", e)))?;
189
190    write!(writer, "[")?;
191
192    let mut first = true;
193    for item_peek in list_peek.iter() {
194        if !first {
195            write!(writer, ",")?;
196        }
197        first = false;
198
199        serialize(&item_peek, writer)?;
200    }
201
202    write!(writer, "]")?;
203
204    Ok(())
205}
206
207/// Serializes a map to JSON
208fn serialize_map<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
209    let map_peek = peek
210        .into_map()
211        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Not a map: {}", e)))?;
212
213    write!(writer, "{{")?;
214
215    let mut first = true;
216    for (key, value) in map_peek.iter() {
217        if !first {
218            write!(writer, ",")?;
219        }
220        first = false;
221
222        // For map, keys must be converted to strings
223        match key.shape().def {
224            Def::Scalar(_) => {
225                // Try to convert key to string
226                if key.shape().is_type::<String>() {
227                    let key_str = key.get::<String>().unwrap();
228                    write_json_string(writer, key_str)?;
229                } else {
230                    // For other scalar types, use their Display implementation
231                    write!(writer, "\"{}\"", key)?;
232                }
233            }
234            _ => {
235                return Err(io::Error::new(
236                    io::ErrorKind::Other,
237                    format!("Map keys must be scalar types, got: {}", key.shape()),
238                ));
239            }
240        }
241
242        write!(writer, ":")?;
243
244        // Write map value
245        serialize(&value, writer)?;
246    }
247
248    write!(writer, "}}")?;
249
250    Ok(())
251}
252
253/// Serializes an enum to JSON
254fn serialize_enum<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
255    let enum_peek = peek
256        .into_enum()
257        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Not an enum: {}", e)))?;
258
259    let variant = enum_peek.active_variant();
260    let variant_name = variant.name;
261
262    // Check if this is a unit variant or a variant with data
263    if variant.data.fields.is_empty() {
264        // Unit variant - just output the name as a string
265        write_json_string(writer, variant_name)
266    } else {
267        // Variant with data - output as an object with a single key
268        write!(writer, "{{")?;
269        write_json_string(writer, variant_name)?;
270        write!(writer, ":")?;
271
272        // If it's a single-field tuple variant, output just the value
273        if variant.data.fields.len() == 1 {
274            let field = enum_peek.field(0).ok_or_else(|| {
275                io::Error::new(io::ErrorKind::Other, "Failed to access enum field")
276            })?;
277            serialize(&field, writer)?;
278        } else {
279            // Multi-field variant - output as an array or object depending on variant type
280            let is_struct = variant
281                .data
282                .fields
283                .iter()
284                .any(|f| !f.name.starts_with("__"));
285
286            if is_struct {
287                // Struct variant - output as an object
288                write!(writer, "{{")?;
289
290                let mut first = true;
291                for i in 0..variant.data.fields.len() {
292                    let field = enum_peek.field(i).ok_or_else(|| {
293                        io::Error::new(
294                            io::ErrorKind::Other,
295                            format!("Failed to access enum field {}", i),
296                        )
297                    })?;
298                    let field_name = variant.data.fields[i].name;
299
300                    if !first {
301                        write!(writer, ",")?;
302                    }
303                    first = false;
304
305                    write_json_string(writer, field_name)?;
306                    write!(writer, ":")?;
307                    serialize(&field, writer)?;
308                }
309
310                write!(writer, "}}")?
311            } else {
312                // Tuple variant - output as an array
313                write!(writer, "[")?;
314
315                let mut first = true;
316                for i in 0..variant.data.fields.len() {
317                    if !first {
318                        write!(writer, ",")?;
319                    }
320                    first = false;
321
322                    let field = enum_peek.field(i).ok_or_else(|| {
323                        io::Error::new(
324                            io::ErrorKind::Other,
325                            format!("Failed to access enum field {}", i),
326                        )
327                    })?;
328                    serialize(&field, writer)?;
329                }
330
331                write!(writer, "]")?;
332            }
333        }
334
335        write!(writer, "}}")?;
336        Ok(())
337    }
338}
339
340/// Serializes an `Option<T>` to JSON
341fn serialize_option<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
342    let option_peek = peek
343        .into_option()
344        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Not an option: {}", e)))?;
345
346    if option_peek.is_none() {
347        write!(writer, "null")
348    } else {
349        let value = option_peek
350            .value()
351            .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed to get option value"))?;
352        serialize(&value, writer)
353    }
354}
355
356/// Properly escapes and writes a JSON string
357fn write_json_string<W: Write>(writer: &mut W, s: &str) -> io::Result<()> {
358    write!(writer, "\"")?;
359
360    for c in s.chars() {
361        match c {
362            '"' => write!(writer, "\\\"")?,
363            '\\' => write!(writer, "\\\\")?,
364            '\n' => write!(writer, "\\n")?,
365            '\r' => write!(writer, "\\r")?,
366            '\t' => write!(writer, "\\t")?,
367            '\u{08}' => write!(writer, "\\b")?,
368            '\u{0C}' => write!(writer, "\\f")?,
369            c if c.is_control() => write!(writer, "\\u{:04x}", c as u32)?,
370            c => write!(writer, "{}", c)?,
371        }
372    }
373
374    write!(writer, "\"")
375}