facet_json/
serialize.rs

1use core::num::NonZero;
2use facet_core::{Def, Facet, FieldAttribute, StructKind};
3use facet_reflect::Peek;
4use std::io::{self, Write};
5
6use crate::First;
7
8/// Serializes a value to JSON
9pub fn to_string<'a, T: Facet<'a>>(value: &T) -> String {
10    let peek = Peek::new(value);
11    let mut output = Vec::new();
12    serialize(&peek, true, &mut output).unwrap();
13    String::from_utf8(output).unwrap()
14}
15
16/// Serializes a Peek instance to JSON
17pub fn peek_to_string(peek: &Peek<'_, '_>) -> String {
18    let mut output = Vec::new();
19    serialize(peek, true, &mut output).unwrap();
20    String::from_utf8(output).unwrap()
21}
22
23/// Serializes a value to a writer in JSON format
24pub fn to_writer<'a, T: Facet<'a>, W: Write>(value: &T, writer: &mut W) -> io::Result<()> {
25    let peek = Peek::new(value);
26    serialize(&peek, true, writer)
27}
28
29/// Serializes a Peek instance to a writer in JSON format
30pub fn peek_to_writer<W: Write>(peek: &Peek<'_, '_>, writer: &mut W) -> io::Result<()> {
31    serialize(peek, true, writer)
32}
33
34/// Task items for the serialization stack.
35#[derive(Debug)]
36enum SerializeTask<'a, 'mem, 'facet> {
37    Value {
38        peek: Peek<'mem, 'facet>,
39        delimit: bool,
40    },
41    ScalarValue(Peek<'mem, 'facet>),
42    StartStruct {
43        struct_peek: Peek<'mem, 'facet>,
44        delimit: bool,
45    },
46    StructField {
47        field_peek: Peek<'mem, 'facet>,
48        field_name: &'a str,
49        should_delimit: bool,
50        is_first: bool,
51    },
52    EndStruct {
53        delimit: bool,
54    },
55    StartList(Peek<'mem, 'facet>),
56    ListItem {
57        item_peek: Peek<'mem, 'facet>,
58        is_first: bool,
59    },
60    EndList,
61    StartTuple(Peek<'mem, 'facet>),
62    TupleItem {
63        item_peek: Peek<'mem, 'facet>,
64        is_first: bool,
65    },
66    EndTuple,
67    StartMap {
68        map_peek: Peek<'mem, 'facet>,
69        delimit: bool,
70    },
71    MapEntry {
72        key: Peek<'mem, 'facet>,
73        value: Peek<'mem, 'facet>,
74        is_first: bool,
75    },
76    EndMap {
77        delimit: bool,
78    },
79    StartEnum {
80        enum_peek: Peek<'mem, 'facet>,
81        // variant_name: &'a str,
82        delimit: bool,
83        // is_empty: bool,
84        // is_struct: bool,
85    },
86    EnumStructField {
87        field: &'a facet_core::Field,
88        field_peek: Peek<'mem, 'facet>,
89        field_name: &'a str,
90        is_first: bool,
91    },
92    EnumTupleField {
93        field: &'a facet_core::Field,
94        field_peek: Peek<'mem, 'facet>,
95        is_first: bool,
96    },
97    EndEnum {
98        delimit: bool,
99        is_struct: bool,
100        is_transparent: bool,
101    },
102    StartOption(Peek<'mem, 'facet>),
103}
104
105/// The core serialization function - iterative approach
106fn serialize<W: Write>(peek: &Peek<'_, '_>, delimit: bool, writer: &mut W) -> io::Result<()> {
107    use facet_core::{
108        StructDef,
109        StructKind::{Tuple, TupleStruct},
110    };
111
112    let mut stack = Vec::new();
113    stack.push(SerializeTask::Value {
114        peek: *peek,
115        delimit,
116    });
117
118    while let Some(task) = stack.pop() {
119        match task {
120            SerializeTask::Value { peek, delimit } => match peek.shape().def {
121                Def::Scalar(_) => {
122                    stack.push(SerializeTask::ScalarValue(peek));
123                }
124                Def::Struct(StructDef {
125                    kind: Tuple | TupleStruct,
126                    ..
127                }) => {
128                    stack.push(SerializeTask::StartTuple(peek));
129                }
130                Def::Struct(_) => {
131                    stack.push(SerializeTask::StartStruct {
132                        struct_peek: peek,
133                        delimit,
134                    });
135                }
136                Def::List(_) => {
137                    stack.push(SerializeTask::StartList(peek));
138                }
139                Def::Map(_) => {
140                    stack.push(SerializeTask::StartMap {
141                        map_peek: peek,
142                        delimit,
143                    });
144                }
145                Def::Enum(_) => {
146                    stack.push(SerializeTask::StartEnum {
147                        enum_peek: peek,
148                        delimit,
149                    });
150                }
151                Def::Option(_) => {
152                    stack.push(SerializeTask::StartOption(peek));
153                }
154                _ => {
155                    return Err(io::Error::new(
156                        io::ErrorKind::Other,
157                        format!("Unsupported type: {}", peek.shape()),
158                    ));
159                }
160            },
161            SerializeTask::ScalarValue(peek) => {
162                serialize_scalar(&peek, writer)?;
163            }
164            SerializeTask::StartStruct {
165                struct_peek,
166                delimit,
167            } => {
168                let struct_peek = struct_peek.into_struct().map_err(|e| {
169                    io::Error::new(io::ErrorKind::Other, format!("Not a struct: {}", e))
170                })?;
171
172                if delimit {
173                    write!(writer, "{{")?;
174                }
175
176                // Process fields in reverse order for the stack
177                let fields = struct_peek
178                    .fields_for_serialize()
179                    .with_first()
180                    .collect::<Vec<_>>();
181
182                stack.push(SerializeTask::EndStruct { delimit });
183
184                for (first, (field, field_peek)) in fields.into_iter().rev() {
185                    let field_name = field.name;
186
187                    // FIXME: flatten is well-known, not arbitrary
188                    let should_delimit = !field.has_arbitrary_attr("flatten");
189
190                    stack.push(SerializeTask::StructField {
191                        field_peek,
192                        field_name,
193                        should_delimit,
194                        is_first: first,
195                    });
196                }
197            }
198            SerializeTask::StructField {
199                field_peek,
200                field_name,
201                should_delimit,
202                is_first,
203                ..
204            } => {
205                if !is_first {
206                    write!(writer, ",")?;
207                }
208
209                // Write field name
210                if should_delimit {
211                    write_json_string(writer, field_name)?;
212                    write!(writer, ":")?;
213                }
214
215                // Push the field value to serialize
216                stack.push(SerializeTask::Value {
217                    peek: field_peek,
218                    delimit: should_delimit,
219                });
220            }
221            SerializeTask::EndStruct { delimit } => {
222                if delimit {
223                    write!(writer, "}}")?;
224                }
225            }
226            SerializeTask::StartList(peek) => {
227                let list_peek = peek.into_list().map_err(|e| {
228                    io::Error::new(io::ErrorKind::Other, format!("Not a list: {}", e))
229                })?;
230
231                write!(writer, "[")?;
232
233                // Process items in reverse order for the stack
234                let items = list_peek.iter().with_first().collect::<Vec<_>>();
235                stack.push(SerializeTask::EndList);
236
237                for (first, item_peek) in items.into_iter().rev() {
238                    stack.push(SerializeTask::ListItem {
239                        item_peek,
240                        is_first: first,
241                    });
242                }
243            }
244            SerializeTask::ListItem {
245                item_peek,
246                is_first,
247            } => {
248                if !is_first {
249                    write!(writer, ",")?;
250                }
251
252                stack.push(SerializeTask::Value {
253                    peek: item_peek,
254                    delimit: true,
255                });
256            }
257            SerializeTask::EndList => {
258                write!(writer, "]")?;
259            }
260            SerializeTask::StartTuple(peek) => {
261                let struct_peek = peek.into_struct().map_err(|e| {
262                    io::Error::new(io::ErrorKind::Other, format!("Not a struct: {}", e))
263                })?;
264
265                write!(writer, "[")?;
266
267                // Process fields in reverse order for the stack
268                let fields = struct_peek
269                    .fields_for_serialize()
270                    .with_first()
271                    .collect::<Vec<_>>();
272
273                stack.push(SerializeTask::EndTuple);
274
275                for (first, (_, item_peek)) in fields.into_iter().rev() {
276                    stack.push(SerializeTask::TupleItem {
277                        item_peek,
278                        is_first: first,
279                    });
280                }
281            }
282            SerializeTask::TupleItem {
283                item_peek,
284                is_first,
285            } => {
286                if !is_first {
287                    write!(writer, ",")?;
288                }
289
290                stack.push(SerializeTask::Value {
291                    peek: item_peek,
292                    delimit: true,
293                });
294            }
295            SerializeTask::EndTuple => {
296                write!(writer, "]")?;
297            }
298            SerializeTask::StartMap { map_peek, delimit } => {
299                let map_peek = map_peek.into_map().map_err(|e| {
300                    io::Error::new(io::ErrorKind::Other, format!("Not a map: {}", e))
301                })?;
302
303                if delimit {
304                    write!(writer, "{{")?;
305                }
306
307                // Process entries in reverse order for the stack
308                let entries = map_peek.iter().with_first().collect::<Vec<_>>();
309                stack.push(SerializeTask::EndMap { delimit });
310
311                for (first, (key, value)) in entries.into_iter().rev() {
312                    stack.push(SerializeTask::MapEntry {
313                        key,
314                        value,
315                        is_first: first,
316                    });
317                }
318            }
319            SerializeTask::MapEntry {
320                key,
321                value,
322                is_first,
323            } => {
324                if !is_first {
325                    write!(writer, ",")?;
326                }
327
328                // For map, keys must be converted to strings
329                match key.shape().def {
330                    Def::Scalar(_) => {
331                        // Try to convert key to string
332                        if key.shape().is_type::<String>() {
333                            let key_str = key.get::<String>().unwrap();
334                            write_json_string(writer, key_str)?;
335                        } else {
336                            // For other scalar types, use their Display implementation
337                            write!(writer, "\"{}\"", key)?;
338                        }
339                    }
340                    _ => {
341                        return Err(io::Error::new(
342                            io::ErrorKind::Other,
343                            format!("Map keys must be scalar types, got: {}", key.shape()),
344                        ));
345                    }
346                }
347
348                write!(writer, ":")?;
349
350                // Push the value to serialize
351                stack.push(SerializeTask::Value {
352                    peek: value,
353                    delimit: true,
354                });
355            }
356            SerializeTask::EndMap { delimit } => {
357                if delimit {
358                    write!(writer, "}}")?;
359                }
360            }
361            SerializeTask::StartEnum { enum_peek, delimit } => {
362                let enum_peek = enum_peek.into_enum().map_err(|e| {
363                    io::Error::new(io::ErrorKind::Other, format!("Not a map: {}", e))
364                })?;
365                let variant = enum_peek.active_variant();
366                let variant_name = variant.name;
367                let is_empty = variant.data.fields.is_empty();
368                let is_struct = variant.data.kind == StructKind::Struct;
369                if is_empty {
370                    // Unit variant - just output the name as a string
371                    write_json_string(writer, variant_name)?;
372                } else {
373                    // Variant with data - output as an object with a single key
374                    if delimit {
375                        write!(writer, "{{")?;
376                    }
377                    write_json_string(writer, variant_name)?;
378                    write!(writer, ":")?;
379
380                    let is_transparent = crate::variant_is_transparent(enum_peek.active_variant());
381
382                    stack.push(SerializeTask::EndEnum {
383                        delimit,
384                        is_struct,
385                        is_transparent,
386                    });
387
388                    if is_struct {
389                        // Struct variant - output as an object
390                        write!(writer, "{{")?;
391
392                        // Process fields in reverse order for the stack
393                        let fields = enum_peek
394                            .fields_for_serialize()
395                            .with_first()
396                            .collect::<Vec<_>>();
397
398                        for (first, (field, field_peek)) in fields.into_iter().rev() {
399                            stack.push(SerializeTask::EnumStructField {
400                                field,
401                                field_peek,
402                                field_name: field.name,
403                                is_first: first,
404                            });
405                        }
406                    } else if is_transparent {
407                        // Transparent variant - output the field directly
408                        if let Some(field) = enum_peek.field(0) {
409                            stack.push(SerializeTask::Value {
410                                peek: field,
411                                delimit: true,
412                            });
413                        } else {
414                            return Err(io::Error::new(
415                                io::ErrorKind::Other,
416                                "Failed to access enum field",
417                            ));
418                        }
419                    } else {
420                        // Tuple variant - output as an array
421                        write!(writer, "[")?;
422
423                        // Process fields in reverse order for the stack
424                        let fields = enum_peek
425                            .fields_for_serialize()
426                            .with_first()
427                            .collect::<Vec<_>>();
428
429                        for (first, (field, field_peek)) in fields.into_iter().rev() {
430                            stack.push(SerializeTask::EnumTupleField {
431                                field,
432                                field_peek,
433                                is_first: first,
434                            });
435                        }
436                    }
437                }
438            }
439            SerializeTask::EnumStructField {
440                field_peek,
441                field_name,
442                is_first,
443                field,
444            } => {
445                if !is_first {
446                    write!(writer, ",")?;
447                }
448
449                let should_delimit = field
450                    .attributes
451                    .iter()
452                    .any(|&attr| attr == FieldAttribute::Arbitrary("flatten"));
453
454                write_json_string(writer, field_name)?;
455                write!(writer, ":")?;
456
457                stack.push(SerializeTask::Value {
458                    peek: field_peek,
459                    delimit: should_delimit,
460                });
461            }
462            SerializeTask::EnumTupleField {
463                field_peek,
464                is_first,
465                field,
466                ..
467            } => {
468                if !is_first {
469                    write!(writer, ",")?;
470                }
471
472                let should_delimit = field
473                    .attributes
474                    .iter()
475                    .any(|&attr| attr == FieldAttribute::Arbitrary("flatten"));
476
477                stack.push(SerializeTask::Value {
478                    peek: field_peek,
479                    delimit: should_delimit,
480                });
481            }
482            SerializeTask::EndEnum {
483                delimit,
484                is_struct,
485                is_transparent,
486            } => {
487                if is_struct {
488                    write!(writer, "}}")?;
489                } else if !is_transparent {
490                    write!(writer, "]")?;
491                }
492
493                if delimit {
494                    write!(writer, "}}")?;
495                }
496            }
497            SerializeTask::StartOption(peek) => {
498                let option_peek = peek.into_option().map_err(|e| {
499                    io::Error::new(io::ErrorKind::Other, format!("Not an option: {}", e))
500                })?;
501
502                if option_peek.is_none() {
503                    write!(writer, "null")?;
504                } else {
505                    let value = option_peek.value().ok_or_else(|| {
506                        io::Error::new(io::ErrorKind::Other, "Failed to get option value")
507                    })?;
508
509                    stack.push(SerializeTask::Value {
510                        peek: value,
511                        delimit: true,
512                    });
513                }
514            }
515        }
516    }
517
518    Ok(())
519}
520
521/// Serializes a scalar value to JSON
522fn serialize_scalar<W: Write>(peek: &Peek<'_, '_>, writer: &mut W) -> io::Result<()> {
523    // Handle basic scalar types
524    if peek.shape().is_type::<bool>() {
525        let value = peek.get::<bool>().unwrap();
526        write!(writer, "{}", if *value { "true" } else { "false" })
527    } else if peek.shape().is_type::<String>() {
528        let value = peek.get::<String>().unwrap();
529        write_json_string(writer, value)
530    } else if peek.shape().is_type::<&str>() {
531        let value = peek.get::<&str>().unwrap();
532        write_json_string(writer, value)
533    } else if peek.shape().is_type::<alloc::borrow::Cow<'_, str>>() {
534        let value = peek.get::<alloc::borrow::Cow<'_, str>>().unwrap();
535        write_json_string(writer, value)
536    }
537    // Integer types
538    else if peek.shape().is_type::<u8>() {
539        let value = peek.get::<u8>().unwrap();
540        write!(writer, "{}", value)
541    } else if peek.shape().is_type::<u16>() {
542        let value = peek.get::<u16>().unwrap();
543        write!(writer, "{}", value)
544    } else if peek.shape().is_type::<u32>() {
545        let value = peek.get::<u32>().unwrap();
546        write!(writer, "{}", value)
547    } else if peek.shape().is_type::<u64>() {
548        let value = peek.get::<u64>().unwrap();
549        write!(writer, "{}", value)
550    } else if peek.shape().is_type::<usize>() {
551        let value = peek.get::<usize>().unwrap();
552        write!(writer, "{}", value)
553    } else if peek.shape().is_type::<i8>() {
554        let value = peek.get::<i8>().unwrap();
555        write!(writer, "{}", value)
556    } else if peek.shape().is_type::<i16>() {
557        let value = peek.get::<i16>().unwrap();
558        write!(writer, "{}", value)
559    } else if peek.shape().is_type::<i32>() {
560        let value = peek.get::<i32>().unwrap();
561        write!(writer, "{}", value)
562    } else if peek.shape().is_type::<i64>() {
563        let value = peek.get::<i64>().unwrap();
564        write!(writer, "{}", value)
565    } else if peek.shape().is_type::<isize>() {
566        let value = peek.get::<isize>().unwrap();
567        write!(writer, "{}", value)
568    }
569    // NonZero types
570    else if peek.shape().is_type::<NonZero<u8>>() {
571        let value = peek.get::<NonZero<u8>>().unwrap();
572        write!(writer, "{}", value)
573    } else if peek.shape().is_type::<NonZero<u16>>() {
574        let value = peek.get::<NonZero<u16>>().unwrap();
575        write!(writer, "{}", value)
576    } else if peek.shape().is_type::<NonZero<u32>>() {
577        let value = peek.get::<NonZero<u32>>().unwrap();
578        write!(writer, "{}", value)
579    } else if peek.shape().is_type::<NonZero<u64>>() {
580        let value = peek.get::<NonZero<u64>>().unwrap();
581        write!(writer, "{}", value)
582    } else if peek.shape().is_type::<NonZero<usize>>() {
583        let value = peek.get::<NonZero<usize>>().unwrap();
584        write!(writer, "{}", value)
585    } else if peek.shape().is_type::<NonZero<i8>>() {
586        let value = peek.get::<NonZero<i8>>().unwrap();
587        write!(writer, "{}", value)
588    } else if peek.shape().is_type::<NonZero<i16>>() {
589        let value = peek.get::<NonZero<i16>>().unwrap();
590        write!(writer, "{}", value)
591    } else if peek.shape().is_type::<NonZero<i32>>() {
592        let value = peek.get::<NonZero<i32>>().unwrap();
593        write!(writer, "{}", value)
594    } else if peek.shape().is_type::<NonZero<i64>>() {
595        let value = peek.get::<NonZero<i64>>().unwrap();
596        write!(writer, "{}", value)
597    } else if peek.shape().is_type::<NonZero<isize>>() {
598        let value = peek.get::<NonZero<isize>>().unwrap();
599        write!(writer, "{}", value)
600    }
601    // Float types
602    else if peek.shape().is_type::<f32>() {
603        let value = peek.get::<f32>().unwrap();
604        write!(writer, "{}", value)
605    } else if peek.shape().is_type::<f64>() {
606        let value = peek.get::<f64>().unwrap();
607        write!(writer, "{}", value)
608    } else {
609        Err(io::Error::new(
610            io::ErrorKind::Other,
611            format!("Unsupported scalar type: {}", peek.shape()),
612        ))
613    }
614}
615
616/// Properly escapes and writes a JSON string
617fn write_json_string<W: Write>(writer: &mut W, s: &str) -> io::Result<()> {
618    write!(writer, "\"")?;
619
620    for c in s.chars() {
621        match c {
622            '"' => write!(writer, "\\\"")?,
623            '\\' => write!(writer, "\\\\")?,
624            '\n' => write!(writer, "\\n")?,
625            '\r' => write!(writer, "\\r")?,
626            '\t' => write!(writer, "\\t")?,
627            '\u{08}' => write!(writer, "\\b")?,
628            '\u{0C}' => write!(writer, "\\f")?,
629            c if c.is_control() => write!(writer, "\\u{:04x}", c as u32)?,
630            c => write!(writer, "{}", c)?,
631        }
632    }
633
634    write!(writer, "\"")
635}