facet_json/
serialize.rs

1use core::num::NonZero;
2use facet_core::{Def, Facet};
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        // Write field name
159        write_json_string(writer, field.name)?;
160        write!(writer, ":")?;
161
162        // Write field value
163        serialize(&field_peek, writer)?;
164    }
165
166    write!(writer, "}}")?;
167
168    Ok(())
169}
170
171/// Serializes a list to JSON
172fn serialize_list<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
173    let list_peek = peek
174        .into_list()
175        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Not a list: {}", e)))?;
176
177    write!(writer, "[")?;
178
179    let mut first = true;
180    for item_peek in list_peek.iter() {
181        if !first {
182            write!(writer, ",")?;
183        }
184        first = false;
185
186        serialize(&item_peek, writer)?;
187    }
188
189    write!(writer, "]")?;
190
191    Ok(())
192}
193
194/// Serializes a map to JSON
195fn serialize_map<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
196    let map_peek = peek
197        .into_map()
198        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Not a map: {}", e)))?;
199
200    write!(writer, "{{")?;
201
202    let mut first = true;
203    for (key, value) in map_peek.iter() {
204        if !first {
205            write!(writer, ",")?;
206        }
207        first = false;
208
209        // For map, keys must be converted to strings
210        match key.shape().def {
211            Def::Scalar(_) => {
212                // Try to convert key to string
213                if key.shape().is_type::<String>() {
214                    let key_str = key.get::<String>().unwrap();
215                    write_json_string(writer, key_str)?;
216                } else {
217                    // For other scalar types, use their Display implementation
218                    write!(writer, "\"{}\"", key)?;
219                }
220            }
221            _ => {
222                return Err(io::Error::new(
223                    io::ErrorKind::Other,
224                    format!("Map keys must be scalar types, got: {}", key.shape()),
225                ));
226            }
227        }
228
229        write!(writer, ":")?;
230
231        // Write map value
232        serialize(&value, writer)?;
233    }
234
235    write!(writer, "}}")?;
236
237    Ok(())
238}
239
240/// Serializes an enum to JSON
241fn serialize_enum<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
242    let enum_peek = peek
243        .into_enum()
244        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Not an enum: {}", e)))?;
245
246    let variant = enum_peek.active_variant();
247    let variant_name = variant.name;
248
249    // Check if this is a unit variant or a variant with data
250    if variant.data.fields.is_empty() {
251        // Unit variant - just output the name as a string
252        write_json_string(writer, variant_name)
253    } else {
254        // Variant with data - output as an object with a single key
255        write!(writer, "{{")?;
256        write_json_string(writer, variant_name)?;
257        write!(writer, ":")?;
258
259        // If it's a single-field tuple variant, output just the value
260        if variant.data.fields.len() == 1 {
261            let field = enum_peek.field(0).ok_or_else(|| {
262                io::Error::new(io::ErrorKind::Other, "Failed to access enum field")
263            })?;
264            serialize(&field, writer)?;
265        } else {
266            // Multi-field variant - output as an array or object depending on variant type
267            let is_struct = variant
268                .data
269                .fields
270                .iter()
271                .any(|f| !f.name.starts_with("__"));
272
273            if is_struct {
274                // Struct variant - output as an object
275                write!(writer, "{{")?;
276
277                let mut first = true;
278                for i in 0..variant.data.fields.len() {
279                    let field = enum_peek.field(i).ok_or_else(|| {
280                        io::Error::new(
281                            io::ErrorKind::Other,
282                            format!("Failed to access enum field {}", i),
283                        )
284                    })?;
285                    let field_name = variant.data.fields[i].name;
286
287                    if !first {
288                        write!(writer, ",")?;
289                    }
290                    first = false;
291
292                    write_json_string(writer, field_name)?;
293                    write!(writer, ":")?;
294                    serialize(&field, writer)?;
295                }
296
297                write!(writer, "}}")?
298            } else {
299                // Tuple variant - output as an array
300                write!(writer, "[")?;
301
302                let mut first = true;
303                for i in 0..variant.data.fields.len() {
304                    if !first {
305                        write!(writer, ",")?;
306                    }
307                    first = false;
308
309                    let field = enum_peek.field(i).ok_or_else(|| {
310                        io::Error::new(
311                            io::ErrorKind::Other,
312                            format!("Failed to access enum field {}", i),
313                        )
314                    })?;
315                    serialize(&field, writer)?;
316                }
317
318                write!(writer, "]")?;
319            }
320        }
321
322        write!(writer, "}}")?;
323        Ok(())
324    }
325}
326
327/// Serializes an `Option<T>` to JSON
328fn serialize_option<W: Write>(peek: &Peek<'_>, writer: &mut W) -> io::Result<()> {
329    let option_peek = peek
330        .into_option()
331        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Not an option: {}", e)))?;
332
333    if option_peek.is_none() {
334        write!(writer, "null")
335    } else {
336        let value = option_peek
337            .value()
338            .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed to get option value"))?;
339        serialize(&value, writer)
340    }
341}
342
343/// Properly escapes and writes a JSON string
344fn write_json_string<W: Write>(writer: &mut W, s: &str) -> io::Result<()> {
345    write!(writer, "\"")?;
346
347    for c in s.chars() {
348        match c {
349            '"' => write!(writer, "\\\"")?,
350            '\\' => write!(writer, "\\\\")?,
351            '\n' => write!(writer, "\\n")?,
352            '\r' => write!(writer, "\\r")?,
353            '\t' => write!(writer, "\\t")?,
354            '\u{08}' => write!(writer, "\\b")?,
355            '\u{0C}' => write!(writer, "\\f")?,
356            c if c.is_control() => write!(writer, "\\u{:04x}", c as u32)?,
357            c => write!(writer, "{}", c)?,
358        }
359    }
360
361    write!(writer, "\"")
362}