facet_json/
serialize.rs

1use alloc::string::String;
2use alloc::vec::Vec;
3use facet_core::{Def, DynValueKind, Facet, PointerType, StructKind, Type, UserType};
4use facet_reflect::{FieldItem, HasFields, Peek, ScalarType};
5use log::trace;
6
7use crate::RawJson;
8
9/// Options for JSON serialization.
10#[derive(Debug, Clone)]
11pub struct SerializeOptions {
12    /// Whether to pretty-print with indentation (default: false)
13    pub pretty: bool,
14    /// Indentation string for pretty-printing (default: "  ")
15    pub indent: &'static str,
16}
17
18impl Default for SerializeOptions {
19    fn default() -> Self {
20        Self {
21            pretty: false,
22            indent: "  ",
23        }
24    }
25}
26
27impl SerializeOptions {
28    /// Create new default options (compact output).
29    pub fn new() -> Self {
30        Self::default()
31    }
32
33    /// Enable pretty-printing with default indentation.
34    pub fn pretty(mut self) -> Self {
35        self.pretty = true;
36        self
37    }
38
39    /// Set a custom indentation string (implies pretty-printing).
40    pub fn indent(mut self, indent: &'static str) -> Self {
41        self.indent = indent;
42        self.pretty = true;
43        self
44    }
45
46    /// Get the indent string if pretty-printing is enabled, otherwise None.
47    fn indent_str(&self) -> Option<&str> {
48        if self.pretty { Some(self.indent) } else { None }
49    }
50}
51
52/// Serializes a value implementing `Facet` to a JSON string.
53pub fn to_string<'facet, T: Facet<'facet> + ?Sized>(value: &T) -> String {
54    peek_to_string(Peek::new(value))
55}
56
57/// Serializes a value implementing `Facet` to a pretty-printed JSON string.
58pub fn to_string_pretty<'facet, T: Facet<'facet> + ?Sized>(value: &T) -> String {
59    peek_to_string_pretty(Peek::new(value))
60}
61
62/// Serializes a value implementing `Facet` to a JSON string with custom options.
63///
64/// # Example
65///
66/// ```
67/// use facet::Facet;
68/// use facet_json::{to_string_with_options, SerializeOptions};
69///
70/// #[derive(Facet)]
71/// struct Person {
72///     name: String,
73///     age: u32,
74/// }
75///
76/// let person = Person { name: "Alice".to_string(), age: 30 };
77///
78/// // Compact output
79/// let json = to_string_with_options(&person, &SerializeOptions::default());
80/// assert_eq!(json, r#"{"name":"Alice","age":30}"#);
81///
82/// // Pretty output with tabs
83/// let json = to_string_with_options(&person, &SerializeOptions::default().indent("\t"));
84/// ```
85pub fn to_string_with_options<'facet, T: Facet<'facet> + ?Sized>(
86    value: &T,
87    options: &SerializeOptions,
88) -> String {
89    peek_to_string_with_options(Peek::new(value), options)
90}
91
92/// Serializes a `Peek` instance to a JSON string.
93pub fn peek_to_string<'input, 'facet>(peek: Peek<'input, 'facet>) -> String {
94    peek_to_string_with_options(peek, &SerializeOptions::default())
95}
96
97/// Serializes a `Peek` instance to a pretty-printed JSON string.
98pub fn peek_to_string_pretty<'input, 'facet>(peek: Peek<'input, 'facet>) -> String {
99    peek_to_string_with_options(peek, &SerializeOptions::default().pretty())
100}
101
102/// Serializes a `Peek` instance to a JSON string with custom options.
103pub fn peek_to_string_with_options<'input, 'facet>(
104    peek: Peek<'input, 'facet>,
105    options: &SerializeOptions,
106) -> String {
107    let mut s = Vec::new();
108    peek_to_writer_with_options(peek, &mut s, options).unwrap();
109    String::from_utf8(s).unwrap()
110}
111
112/// Serializes a `Facet` value to JSON and writes it to the given writer.
113pub fn to_writer<'mem, 'facet, T: Facet<'facet>, W: crate::JsonWrite>(
114    value: &'mem T,
115    writer: W,
116) -> Result<(), SerializeError> {
117    peek_to_writer(Peek::new(value), writer)
118}
119
120/// Serializes a `Facet` value to pretty-printed JSON and writes it to the given writer.
121pub fn to_writer_pretty<'mem, 'facet, T: Facet<'facet>, W: crate::JsonWrite>(
122    value: &'mem T,
123    writer: W,
124) -> Result<(), SerializeError> {
125    peek_to_writer_pretty(Peek::new(value), writer)
126}
127
128/// Serializes a `Facet` value to JSON with custom options and writes it to the given writer.
129///
130/// # Example
131///
132/// ```
133/// use facet::Facet;
134/// use facet_json::{to_writer_with_options, SerializeOptions};
135///
136/// #[derive(Facet)]
137/// struct Person {
138///     name: String,
139///     age: u32,
140/// }
141///
142/// let person = Person { name: "Alice".to_string(), age: 30 };
143///
144/// // Compact output (default)
145/// let mut buffer = Vec::new();
146/// to_writer_with_options(&person, &mut buffer, &SerializeOptions::default()).unwrap();
147/// assert_eq!(buffer, br#"{"name":"Alice","age":30}"#);
148///
149/// // Pretty output with default indent
150/// let mut buffer = Vec::new();
151/// to_writer_with_options(&person, &mut buffer, &SerializeOptions::default().pretty()).unwrap();
152///
153/// // Pretty output with custom indent (tabs)
154/// let mut buffer = Vec::new();
155/// to_writer_with_options(&person, &mut buffer, &SerializeOptions::default().indent("\t")).unwrap();
156/// ```
157pub fn to_writer_with_options<'mem, 'facet, T: Facet<'facet>, W: crate::JsonWrite>(
158    value: &'mem T,
159    writer: W,
160    options: &SerializeOptions,
161) -> Result<(), SerializeError> {
162    peek_to_writer_with_options(Peek::new(value), writer, options)
163}
164
165/// Serializes a `Peek` value to JSON and writes it to the given writer.
166pub fn peek_to_writer<'mem, 'facet, W: crate::JsonWrite>(
167    peek: Peek<'mem, 'facet>,
168    writer: W,
169) -> Result<(), SerializeError> {
170    peek_to_writer_with_options(peek, writer, &SerializeOptions::default())
171}
172
173/// Serializes a `Peek` value to pretty-printed JSON and writes it to the given writer.
174pub fn peek_to_writer_pretty<'mem, 'facet, W: crate::JsonWrite>(
175    peek: Peek<'mem, 'facet>,
176    writer: W,
177) -> Result<(), SerializeError> {
178    peek_to_writer_with_options(peek, writer, &SerializeOptions::default().pretty())
179}
180
181/// Serializes a `Peek` value to JSON with custom options and writes it to the given writer.
182pub fn peek_to_writer_with_options<'mem, 'facet, W: crate::JsonWrite>(
183    peek: Peek<'mem, 'facet>,
184    mut writer: W,
185    options: &SerializeOptions,
186) -> Result<(), SerializeError> {
187    serialize_value(peek, None, &mut writer, options.indent_str(), 0)
188}
189
190/// Serializes a `Facet` value to JSON and writes it to a `std::io::Write` writer.
191///
192/// This is a convenience function for users who want to write to standard library
193/// writers like `File`, `TcpStream`, or any other `std::io::Write` implementor.
194///
195/// # Example
196///
197/// ```
198/// use facet::Facet;
199/// use facet_json::to_writer_std;
200///
201/// #[derive(Facet)]
202/// struct Person {
203///     name: String,
204///     age: u32,
205/// }
206///
207/// let person = Person { name: "Alice".to_string(), age: 30 };
208/// let mut buffer = Vec::new();
209/// to_writer_std(&mut buffer, &person).unwrap();
210/// assert_eq!(buffer, br#"{"name":"Alice","age":30}"#);
211/// ```
212#[cfg(feature = "std")]
213pub fn to_writer_std<'mem, 'facet, W: std::io::Write, T: Facet<'facet>>(
214    writer: W,
215    value: &'mem T,
216) -> std::io::Result<()> {
217    peek_to_writer_std(writer, Peek::new(value))
218}
219
220/// Serializes a `Facet` value to pretty-printed JSON and writes it to a `std::io::Write` writer.
221///
222/// This is a convenience function for users who want to write to standard library
223/// writers like `File`, `TcpStream`, or any other `std::io::Write` implementor.
224#[cfg(feature = "std")]
225pub fn to_writer_std_pretty<'mem, 'facet, W: std::io::Write, T: Facet<'facet>>(
226    writer: W,
227    value: &'mem T,
228) -> std::io::Result<()> {
229    peek_to_writer_std_pretty(writer, Peek::new(value))
230}
231
232/// Serializes a `Facet` value to JSON with custom options and writes it to a `std::io::Write` writer.
233///
234/// # Example
235///
236/// ```
237/// use facet::Facet;
238/// use facet_json::{to_writer_std_with_options, SerializeOptions};
239///
240/// #[derive(Facet)]
241/// struct Person {
242///     name: String,
243///     age: u32,
244/// }
245///
246/// let person = Person { name: "Alice".to_string(), age: 30 };
247///
248/// // Compact output
249/// let mut buffer = Vec::new();
250/// to_writer_std_with_options(&mut buffer, &person, &SerializeOptions::default()).unwrap();
251/// assert_eq!(buffer, br#"{"name":"Alice","age":30}"#);
252///
253/// // Pretty output with tabs
254/// let mut buffer = Vec::new();
255/// to_writer_std_with_options(&mut buffer, &person, &SerializeOptions::default().indent("\t")).unwrap();
256/// ```
257#[cfg(feature = "std")]
258pub fn to_writer_std_with_options<'mem, 'facet, W: std::io::Write, T: Facet<'facet>>(
259    writer: W,
260    value: &'mem T,
261    options: &SerializeOptions,
262) -> std::io::Result<()> {
263    peek_to_writer_std_with_options(writer, Peek::new(value), options)
264}
265
266/// Serializes a `Peek` value to JSON and writes it to a `std::io::Write` writer.
267#[cfg(feature = "std")]
268pub fn peek_to_writer_std<'mem, 'facet, W: std::io::Write>(
269    writer: W,
270    peek: Peek<'mem, 'facet>,
271) -> std::io::Result<()> {
272    peek_to_writer_std_with_options(writer, peek, &SerializeOptions::default())
273}
274
275/// Serializes a `Peek` value to pretty-printed JSON and writes it to a `std::io::Write` writer.
276#[cfg(feature = "std")]
277pub fn peek_to_writer_std_pretty<'mem, 'facet, W: std::io::Write>(
278    writer: W,
279    peek: Peek<'mem, 'facet>,
280) -> std::io::Result<()> {
281    peek_to_writer_std_with_options(writer, peek, &SerializeOptions::default().pretty())
282}
283
284/// Serializes a `Peek` value to JSON with custom options and writes it to a `std::io::Write` writer.
285#[cfg(feature = "std")]
286pub fn peek_to_writer_std_with_options<'mem, 'facet, W: std::io::Write>(
287    writer: W,
288    peek: Peek<'mem, 'facet>,
289    options: &SerializeOptions,
290) -> std::io::Result<()> {
291    let mut adapter = StdWriteAdapter::new(writer);
292    let _ = peek_to_writer_with_options(peek, &mut adapter, options);
293    adapter.into_result()
294}
295
296/// Adapter that wraps a `std::io::Write` to implement `JsonWrite`.
297#[cfg(feature = "std")]
298struct StdWriteAdapter<W> {
299    writer: W,
300    error: Option<std::io::Error>,
301}
302
303#[cfg(feature = "std")]
304impl<W: std::io::Write> StdWriteAdapter<W> {
305    fn new(writer: W) -> Self {
306        Self {
307            writer,
308            error: None,
309        }
310    }
311
312    fn into_result(self) -> std::io::Result<()> {
313        match self.error {
314            Some(e) => Err(e),
315            None => Ok(()),
316        }
317    }
318}
319
320#[cfg(feature = "std")]
321impl<W: std::io::Write> crate::JsonWrite for StdWriteAdapter<W> {
322    fn write(&mut self, buf: &[u8]) {
323        if self.error.is_none()
324            && let Err(e) = self.writer.write_all(buf)
325        {
326            self.error = Some(e);
327        }
328    }
329
330    fn reserve(&mut self, _additional: usize) {
331        // std::io::Write doesn't have a reserve method, so this is a no-op
332    }
333}
334
335#[cfg(feature = "std")]
336impl<W: std::io::Write> crate::JsonWrite for &mut StdWriteAdapter<W> {
337    fn write(&mut self, buf: &[u8]) {
338        if self.error.is_none()
339            && let Err(e) = self.writer.write_all(buf)
340        {
341            self.error = Some(e);
342        }
343    }
344
345    fn reserve(&mut self, _additional: usize) {
346        // std::io::Write doesn't have a reserve method, so this is a no-op
347    }
348}
349
350/// Serialization error for json, which cannot fail.
351#[derive(Debug)]
352pub enum SerializeError {}
353
354fn variant_is_newtype_like(variant: &facet_core::Variant) -> bool {
355    matches!(
356        variant.data.kind,
357        StructKind::Tuple | StructKind::TupleStruct
358    ) && variant.data.fields.len() == 1
359}
360
361/// Write indentation for pretty printing
362fn write_indent<W: crate::JsonWrite>(writer: &mut W, indent: Option<&str>, depth: usize) {
363    if let Some(indent_str) = indent {
364        for _ in 0..depth {
365            writer.write(indent_str.as_bytes());
366        }
367    }
368}
369
370/// Write a newline for pretty printing
371fn write_newline<W: crate::JsonWrite>(writer: &mut W, indent: Option<&str>) {
372    if indent.is_some() {
373        writer.write(b"\n");
374    }
375}
376
377/// Write a space after colon for pretty printing
378fn write_colon<W: crate::JsonWrite>(writer: &mut W, indent: Option<&str>) {
379    if indent.is_some() {
380        writer.write(b": ");
381    } else {
382        writer.write(b":");
383    }
384}
385
386fn serialize_value<'mem, 'facet, W: crate::JsonWrite>(
387    peek: Peek<'mem, 'facet>,
388    maybe_field_item: Option<FieldItem>,
389    writer: &mut W,
390    indent: Option<&str>,
391    depth: usize,
392) -> Result<(), SerializeError> {
393    trace!("Serializing a value, shape is {}", peek.shape());
394
395    // Handle custom serialization (field-level proxy)
396    #[cfg(feature = "alloc")]
397    if let Some(fi) = maybe_field_item
398        && fi.field.proxy_convert_out_fn().is_some()
399    {
400        let owned_peek = peek.custom_serialization(fi.field).unwrap();
401        let old_shape = peek.shape();
402        let new_shape = owned_peek.shape();
403        trace!("{old_shape} has custom serialization, serializing as {new_shape} instead");
404        return serialize_value(owned_peek.as_peek(), None, writer, indent, depth);
405    }
406
407    // Handle container-level proxy (applies even in Vec<T>, Option<T>, etc.)
408    #[cfg(feature = "alloc")]
409    if let Ok(Some(owned_peek)) = peek.custom_serialization_from_shape() {
410        let old_shape = peek.shape();
411        let new_shape = owned_peek.shape();
412        trace!("{old_shape} has container-level proxy, serializing as {new_shape} instead");
413        return serialize_value(owned_peek.as_peek(), None, writer, indent, depth);
414    }
415
416    // Handle transparent types
417    if peek.shape().is_transparent() {
418        let old_shape = peek.shape();
419        let ps = peek.into_struct().unwrap();
420        let (field, inner_peek) = ps.fields().next().unwrap();
421        let new_shape = inner_peek.shape();
422        trace!("{old_shape} is transparent, let's serialize the inner {new_shape} instead");
423        return serialize_value(
424            inner_peek,
425            Some(FieldItem::new(field)),
426            writer,
427            indent,
428            depth,
429        );
430    }
431
432    // Handle RawJson - write raw content directly
433    if peek.shape() == RawJson::SHAPE {
434        // SAFETY: We've verified the shape matches RawJson
435        #[allow(unsafe_code)]
436        let raw = unsafe { peek.data().get::<RawJson<'static>>() };
437        writer.write(raw.as_str().as_bytes());
438        return Ok(());
439    }
440
441    trace!(
442        "Matching def={:?}, ty={:?} for shape={}",
443        peek.shape().def,
444        peek.shape().ty,
445        peek.shape()
446    );
447
448    match (peek.shape().def, peek.shape().ty) {
449        (Def::Scalar, _) => {
450            let peek = peek.innermost_peek();
451            serialize_scalar(peek, writer)?;
452        }
453        (Def::List(ld), _) => {
454            if ld.t().is_type::<u8>() && peek.shape().is_type::<Vec<u8>>() {
455                // Special case for Vec<u8> - serialize as array of numbers
456                let bytes = peek.get::<Vec<u8>>().unwrap();
457                serialize_byte_array(bytes, writer, indent, depth)?;
458            } else {
459                let peek_list = peek.into_list_like().unwrap();
460                serialize_array(peek_list.iter(), writer, indent, depth)?;
461            }
462        }
463        (Def::Array(ad), _) => {
464            if ad.t().is_type::<u8>() {
465                let bytes: Vec<u8> = peek
466                    .into_list_like()
467                    .unwrap()
468                    .iter()
469                    .map(|p| *p.get::<u8>().unwrap())
470                    .collect();
471                serialize_byte_array(&bytes, writer, indent, depth)?;
472            } else {
473                let peek_list = peek.into_list_like().unwrap();
474                serialize_array(peek_list.iter(), writer, indent, depth)?;
475            }
476        }
477        (Def::Slice(sd), _) => {
478            if sd.t().is_type::<u8>() {
479                let bytes = peek.get::<[u8]>().unwrap();
480                serialize_byte_array(bytes, writer, indent, depth)?;
481            } else {
482                let peek_list = peek.into_list_like().unwrap();
483                serialize_array(peek_list.iter(), writer, indent, depth)?;
484            }
485        }
486        (Def::Map(_), _) => {
487            let peek_map = peek.into_map().unwrap();
488            writer.write(b"{");
489            let mut first = true;
490            for (key, value) in peek_map.iter() {
491                if !first {
492                    writer.write(b",");
493                }
494                first = false;
495                write_newline(writer, indent);
496                write_indent(writer, indent, depth + 1);
497                serialize_map_key(key, writer)?;
498                write_colon(writer, indent);
499                serialize_value(value, None, writer, indent, depth + 1)?;
500            }
501            if !first {
502                write_newline(writer, indent);
503                write_indent(writer, indent, depth);
504            }
505            writer.write(b"}");
506        }
507        (Def::Set(_), _) => {
508            let peek_set = peek.into_set().unwrap();
509            writer.write(b"[");
510            let mut first = true;
511            for item in peek_set.iter() {
512                if !first {
513                    writer.write(b",");
514                }
515                first = false;
516                write_newline(writer, indent);
517                write_indent(writer, indent, depth + 1);
518                serialize_value(item, None, writer, indent, depth + 1)?;
519            }
520            if !first {
521                write_newline(writer, indent);
522                write_indent(writer, indent, depth);
523            }
524            writer.write(b"]");
525        }
526        (Def::Option(_), _) => {
527            let opt = peek.into_option().unwrap();
528            if let Some(inner_peek) = opt.value() {
529                serialize_value(inner_peek, None, writer, indent, depth)?;
530            } else {
531                writer.write(b"null");
532            }
533        }
534        (Def::Pointer(_), _) => {
535            let sp = peek.into_pointer().unwrap();
536            if let Some(inner_peek) = sp.borrow_inner() {
537                serialize_value(inner_peek, None, writer, indent, depth)?;
538            } else {
539                panic!(
540                    "Smart pointer without borrow support or with opaque pointee cannot be serialized"
541                );
542            }
543        }
544        (_, Type::User(UserType::Struct(sd))) => {
545            trace!("Serializing struct: shape={}", peek.shape());
546            trace!(
547                "  Struct details: kind={:?}, field_count={}",
548                sd.kind,
549                sd.fields.len()
550            );
551
552            match sd.kind {
553                StructKind::Unit => {
554                    writer.write(b"null");
555                }
556                StructKind::Tuple => {
557                    let peek_struct = peek.into_struct().unwrap();
558                    writer.write(b"[");
559                    let mut first = true;
560                    for (field, value) in peek_struct.fields() {
561                        if !first {
562                            writer.write(b",");
563                        }
564                        first = false;
565                        write_newline(writer, indent);
566                        write_indent(writer, indent, depth + 1);
567                        serialize_value(
568                            value,
569                            Some(FieldItem::new(field)),
570                            writer,
571                            indent,
572                            depth + 1,
573                        )?;
574                    }
575                    if !first {
576                        write_newline(writer, indent);
577                        write_indent(writer, indent, depth);
578                    }
579                    writer.write(b"]");
580                }
581                StructKind::TupleStruct => {
582                    let peek_struct = peek.into_struct().unwrap();
583                    writer.write(b"[");
584                    let mut first = true;
585                    for (field_item, value) in peek_struct.fields_for_serialize() {
586                        if !first {
587                            writer.write(b",");
588                        }
589                        first = false;
590                        write_newline(writer, indent);
591                        write_indent(writer, indent, depth + 1);
592                        serialize_value(value, Some(field_item), writer, indent, depth + 1)?;
593                    }
594                    if !first {
595                        write_newline(writer, indent);
596                        write_indent(writer, indent, depth);
597                    }
598                    writer.write(b"]");
599                }
600                StructKind::Struct => {
601                    let peek_struct = peek.into_struct().unwrap();
602                    writer.write(b"{");
603                    let mut first = true;
604                    for (field_item, value) in peek_struct.fields_for_serialize() {
605                        if !first {
606                            writer.write(b",");
607                        }
608                        first = false;
609                        write_newline(writer, indent);
610                        write_indent(writer, indent, depth + 1);
611                        crate::write_json_string(writer, field_item.name);
612                        write_colon(writer, indent);
613                        serialize_value(value, Some(field_item), writer, indent, depth + 1)?;
614                    }
615                    if !first {
616                        write_newline(writer, indent);
617                        write_indent(writer, indent, depth);
618                    }
619                    writer.write(b"}");
620                }
621            }
622        }
623        (_, Type::User(UserType::Enum(_))) => {
624            let shape = peek.shape();
625            let peek_enum = peek.into_enum().unwrap();
626            let variant = peek_enum
627                .active_variant()
628                .expect("Failed to get active variant");
629            let variant_index = peek_enum
630                .variant_index()
631                .expect("Failed to get variant index");
632            trace!("Active variant index is {variant_index}, variant is {variant:?}");
633
634            // Determine enum tagging strategy
635            let is_untagged = shape.is_untagged();
636            let tag_field = shape.get_tag_attr();
637            let content_field = shape.get_content_attr();
638
639            if is_untagged {
640                // Untagged: serialize content directly without any tag
641                serialize_enum_content(&peek_enum, variant, writer, indent, depth)?;
642            } else if let Some(tag) = tag_field {
643                if let Some(content) = content_field {
644                    // Adjacently tagged: {"tag": "Variant", "content": ...}
645                    writer.write(b"{");
646                    write_newline(writer, indent);
647                    write_indent(writer, indent, depth + 1);
648                    crate::write_json_string(writer, tag);
649                    write_colon(writer, indent);
650                    crate::write_json_string(writer, variant.name);
651
652                    // Only include content field if variant has data
653                    if !variant.data.fields.is_empty() {
654                        writer.write(b",");
655                        write_newline(writer, indent);
656                        write_indent(writer, indent, depth + 1);
657                        crate::write_json_string(writer, content);
658                        write_colon(writer, indent);
659                        serialize_enum_content(&peek_enum, variant, writer, indent, depth + 1)?;
660                    }
661
662                    write_newline(writer, indent);
663                    write_indent(writer, indent, depth);
664                    writer.write(b"}");
665                } else {
666                    // Internally tagged: {"tag": "Variant", ...fields...}
667                    writer.write(b"{");
668                    write_newline(writer, indent);
669                    write_indent(writer, indent, depth + 1);
670                    crate::write_json_string(writer, tag);
671                    write_colon(writer, indent);
672                    crate::write_json_string(writer, variant.name);
673
674                    // Add struct fields at same level as tag
675                    for (field_item, field_peek) in peek_enum.fields_for_serialize() {
676                        writer.write(b",");
677                        write_newline(writer, indent);
678                        write_indent(writer, indent, depth + 1);
679                        crate::write_json_string(writer, field_item.name);
680                        write_colon(writer, indent);
681                        serialize_value(field_peek, Some(field_item), writer, indent, depth + 1)?;
682                    }
683
684                    write_newline(writer, indent);
685                    write_indent(writer, indent, depth);
686                    writer.write(b"}");
687                }
688            } else {
689                // Externally tagged (default): {"Variant": content} or "Variant" for unit
690                let flattened = maybe_field_item.map(|fi| fi.flattened).unwrap_or_default();
691
692                if variant.data.fields.is_empty() {
693                    // Unit variant - just the name as a string
694                    crate::write_json_string(writer, variant.name);
695                } else {
696                    if !flattened {
697                        // Wrap in object with variant name as key
698                        writer.write(b"{");
699                        write_newline(writer, indent);
700                        write_indent(writer, indent, depth + 1);
701                        crate::write_json_string(writer, variant.name);
702                        write_colon(writer, indent);
703                    }
704
705                    let inner_depth = if flattened { depth } else { depth + 1 };
706                    serialize_enum_content(&peek_enum, variant, writer, indent, inner_depth)?;
707
708                    if !flattened {
709                        write_newline(writer, indent);
710                        write_indent(writer, indent, depth);
711                        writer.write(b"}");
712                    }
713                }
714            }
715        }
716        (Def::DynamicValue(_), _) => {
717            let dyn_val = peek.into_dynamic_value().unwrap();
718            serialize_dynamic_value(dyn_val, writer, indent, depth)?;
719        }
720        (_, Type::Pointer(pointer_type)) => {
721            if let Some(str_value) = peek.as_str() {
722                crate::write_json_string(writer, str_value);
723            } else if let Some(bytes) = peek.as_bytes() {
724                serialize_byte_array(bytes, writer, indent, depth)?;
725            } else if let PointerType::Function(_) = pointer_type {
726                writer.write(b"null");
727            } else {
728                let innermost = peek.innermost_peek();
729                if innermost.shape() != peek.shape() {
730                    serialize_value(innermost, None, writer, indent, depth)?;
731                } else {
732                    writer.write(b"null");
733                }
734            }
735        }
736        _ => {
737            trace!(
738                "Unhandled type: {:?}, falling back to null",
739                peek.shape().ty
740            );
741            writer.write(b"null");
742        }
743    }
744
745    Ok(())
746}
747
748/// Serialize a dynamic value (like `facet_value::Value`) to JSON
749fn serialize_dynamic_value<'mem, 'facet, W: crate::JsonWrite>(
750    dyn_val: facet_reflect::PeekDynamicValue<'mem, 'facet>,
751    writer: &mut W,
752    indent: Option<&str>,
753    depth: usize,
754) -> Result<(), SerializeError> {
755    match dyn_val.kind() {
756        DynValueKind::Null => {
757            writer.write(b"null");
758        }
759        DynValueKind::Bool => {
760            if let Some(b) = dyn_val.as_bool() {
761                writer.write(if b { b"true" } else { b"false" });
762            } else {
763                writer.write(b"null");
764            }
765        }
766        DynValueKind::Number => {
767            // Try i64 first (most common for integers), then u64 (for large unsigned), then f64
768            if let Some(n) = dyn_val.as_i64() {
769                writer.write(itoa::Buffer::new().format(n).as_bytes());
770            } else if let Some(n) = dyn_val.as_u64() {
771                writer.write(itoa::Buffer::new().format(n).as_bytes());
772            } else if let Some(n) = dyn_val.as_f64() {
773                let mut buf = ryu::Buffer::new();
774                writer.write(buf.format(n).as_bytes());
775            } else {
776                writer.write(b"null");
777            }
778        }
779        DynValueKind::String => {
780            if let Some(s) = dyn_val.as_str() {
781                crate::write_json_string(writer, s);
782            } else {
783                writer.write(b"null");
784            }
785        }
786        DynValueKind::Bytes => {
787            // Serialize bytes as an array of numbers (JSON doesn't have native bytes)
788            if let Some(bytes) = dyn_val.as_bytes() {
789                serialize_byte_array(bytes, writer, indent, depth)?;
790            } else {
791                writer.write(b"null");
792            }
793        }
794        DynValueKind::Array => {
795            let len = dyn_val.array_len().unwrap_or(0);
796            if len == 0 {
797                writer.write(b"[]");
798            } else {
799                writer.write(b"[");
800                for idx in 0..len {
801                    if idx > 0 {
802                        writer.write(b",");
803                    }
804                    write_newline(writer, indent);
805                    write_indent(writer, indent, depth + 1);
806                    if let Some(elem) = dyn_val.array_get(idx) {
807                        serialize_value(elem, None, writer, indent, depth + 1)?;
808                    } else {
809                        writer.write(b"null");
810                    }
811                }
812                write_newline(writer, indent);
813                write_indent(writer, indent, depth);
814                writer.write(b"]");
815            }
816        }
817        DynValueKind::Object => {
818            let len = dyn_val.object_len().unwrap_or(0);
819            if len == 0 {
820                writer.write(b"{}");
821            } else {
822                writer.write(b"{");
823                for idx in 0..len {
824                    if idx > 0 {
825                        writer.write(b",");
826                    }
827                    write_newline(writer, indent);
828                    write_indent(writer, indent, depth + 1);
829                    if let Some((key, val)) = dyn_val.object_get_entry(idx) {
830                        crate::write_json_string(writer, key);
831                        write_colon(writer, indent);
832                        serialize_value(val, None, writer, indent, depth + 1)?;
833                    }
834                }
835                write_newline(writer, indent);
836                write_indent(writer, indent, depth);
837                writer.write(b"}");
838            }
839        }
840        DynValueKind::DateTime => {
841            // Serialize datetime as ISO 8601 string
842            if let Some((year, month, day, hour, minute, second, nanos, kind)) =
843                dyn_val.as_datetime()
844            {
845                use facet_core::DynDateTimeKind;
846                let mut buf = String::new();
847                use core::fmt::Write;
848
849                match kind {
850                    DynDateTimeKind::LocalDate => {
851                        write!(buf, "{year:04}-{month:02}-{day:02}").unwrap();
852                    }
853                    DynDateTimeKind::LocalTime => {
854                        if nanos > 0 {
855                            write!(buf, "{hour:02}:{minute:02}:{second:02}.{nanos:09}").unwrap();
856                            // Trim trailing zeros from nanos
857                            while buf.ends_with('0') {
858                                buf.pop();
859                            }
860                        } else {
861                            write!(buf, "{hour:02}:{minute:02}:{second:02}").unwrap();
862                        }
863                    }
864                    DynDateTimeKind::LocalDateTime => {
865                        if nanos > 0 {
866                            write!(
867                                buf,
868                                "{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}.{nanos:09}"
869                            )
870                            .unwrap();
871                            while buf.ends_with('0') {
872                                buf.pop();
873                            }
874                        } else {
875                            write!(
876                                buf,
877                                "{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}"
878                            )
879                            .unwrap();
880                        }
881                    }
882                    DynDateTimeKind::Offset { offset_minutes } => {
883                        if nanos > 0 {
884                            write!(
885                                buf,
886                                "{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}.{nanos:09}"
887                            )
888                            .unwrap();
889                            while buf.ends_with('0') {
890                                buf.pop();
891                            }
892                        } else {
893                            write!(
894                                buf,
895                                "{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}"
896                            )
897                            .unwrap();
898                        }
899                        if offset_minutes == 0 {
900                            buf.push('Z');
901                        } else {
902                            let sign = if offset_minutes >= 0 { '+' } else { '-' };
903                            let abs_offset = offset_minutes.abs();
904                            let offset_hours = abs_offset / 60;
905                            let offset_mins = abs_offset % 60;
906                            write!(buf, "{sign}{offset_hours:02}:{offset_mins:02}").unwrap();
907                        }
908                    }
909                }
910                crate::write_json_string(writer, &buf);
911            } else {
912                writer.write(b"null");
913            }
914        }
915        DynValueKind::QName | DynValueKind::Uuid => {
916            // These are typically string-like; try to get a string representation
917            // For now, serialize as null since we don't have a standard getter
918            writer.write(b"null");
919        }
920    }
921    Ok(())
922}
923
924/// Serialize a map key - JSON requires object keys to be strings
925fn serialize_map_key<W: crate::JsonWrite>(
926    peek: Peek<'_, '_>,
927    writer: &mut W,
928) -> Result<(), SerializeError> {
929    // First try as_str() which handles &str, String, Cow<str>, etc uniformly
930    if let Some(s) = peek.as_str() {
931        crate::write_json_string(writer, s);
932        return Ok(());
933    }
934
935    let peek = peek.innermost_peek();
936    match peek.scalar_type() {
937        // For numeric types, convert to string representation
938        Some(ScalarType::U8) => {
939            let v = *peek.get::<u8>().unwrap();
940            writer.write(b"\"");
941            writer.write(itoa::Buffer::new().format(v).as_bytes());
942            writer.write(b"\"");
943        }
944        Some(ScalarType::U16) => {
945            let v = *peek.get::<u16>().unwrap();
946            writer.write(b"\"");
947            writer.write(itoa::Buffer::new().format(v).as_bytes());
948            writer.write(b"\"");
949        }
950        Some(ScalarType::U32) => {
951            let v = *peek.get::<u32>().unwrap();
952            writer.write(b"\"");
953            writer.write(itoa::Buffer::new().format(v).as_bytes());
954            writer.write(b"\"");
955        }
956        Some(ScalarType::U64) => {
957            let v = *peek.get::<u64>().unwrap();
958            writer.write(b"\"");
959            writer.write(itoa::Buffer::new().format(v).as_bytes());
960            writer.write(b"\"");
961        }
962        Some(ScalarType::U128) => {
963            let v = *peek.get::<u128>().unwrap();
964            writer.write(b"\"");
965            writer.write(itoa::Buffer::new().format(v).as_bytes());
966            writer.write(b"\"");
967        }
968        Some(ScalarType::USize) => {
969            let v = *peek.get::<usize>().unwrap();
970            writer.write(b"\"");
971            writer.write(itoa::Buffer::new().format(v).as_bytes());
972            writer.write(b"\"");
973        }
974        Some(ScalarType::I8) => {
975            let v = *peek.get::<i8>().unwrap();
976            writer.write(b"\"");
977            writer.write(itoa::Buffer::new().format(v).as_bytes());
978            writer.write(b"\"");
979        }
980        Some(ScalarType::I16) => {
981            let v = *peek.get::<i16>().unwrap();
982            writer.write(b"\"");
983            writer.write(itoa::Buffer::new().format(v).as_bytes());
984            writer.write(b"\"");
985        }
986        Some(ScalarType::I32) => {
987            let v = *peek.get::<i32>().unwrap();
988            writer.write(b"\"");
989            writer.write(itoa::Buffer::new().format(v).as_bytes());
990            writer.write(b"\"");
991        }
992        Some(ScalarType::I64) => {
993            let v = *peek.get::<i64>().unwrap();
994            writer.write(b"\"");
995            writer.write(itoa::Buffer::new().format(v).as_bytes());
996            writer.write(b"\"");
997        }
998        Some(ScalarType::I128) => {
999            let v = *peek.get::<i128>().unwrap();
1000            writer.write(b"\"");
1001            writer.write(itoa::Buffer::new().format(v).as_bytes());
1002            writer.write(b"\"");
1003        }
1004        Some(ScalarType::ISize) => {
1005            let v = *peek.get::<isize>().unwrap();
1006            writer.write(b"\"");
1007            writer.write(itoa::Buffer::new().format(v).as_bytes());
1008            writer.write(b"\"");
1009        }
1010        _ => {
1011            // Fallback: use Display if available
1012            if peek.shape().vtable.has_display() {
1013                crate::write_json_string(writer, &alloc::format!("{peek}"));
1014            } else {
1015                panic!("Unsupported map key type: {}", peek.shape())
1016            }
1017        }
1018    }
1019    Ok(())
1020}
1021
1022fn serialize_scalar<W: crate::JsonWrite>(
1023    peek: Peek<'_, '_>,
1024    writer: &mut W,
1025) -> Result<(), SerializeError> {
1026    match peek.scalar_type() {
1027        Some(ScalarType::Unit) => writer.write(b"null"),
1028        Some(ScalarType::Bool) => {
1029            let v = *peek.get::<bool>().unwrap();
1030            writer.write(if v { b"true" } else { b"false" });
1031        }
1032        Some(ScalarType::Char) => {
1033            let c = *peek.get::<char>().unwrap();
1034            writer.write(b"\"");
1035            crate::write_json_escaped_char(writer, c);
1036            writer.write(b"\"");
1037        }
1038        Some(ScalarType::Str) => {
1039            crate::write_json_string(writer, peek.get::<str>().unwrap());
1040        }
1041        Some(ScalarType::String) => {
1042            crate::write_json_string(writer, peek.get::<String>().unwrap());
1043        }
1044        Some(ScalarType::CowStr) => {
1045            // SAFETY: We've verified the scalar type matches CowStr
1046            #[allow(unsafe_code)]
1047            let cow_str = unsafe { peek.data().get::<alloc::borrow::Cow<'static, str>>() };
1048            crate::write_json_string(writer, cow_str.as_ref());
1049        }
1050        Some(ScalarType::F32) => {
1051            let v = *peek.get::<f32>().unwrap();
1052            writer.write(ryu::Buffer::new().format(v).as_bytes());
1053        }
1054        Some(ScalarType::F64) => {
1055            let v = *peek.get::<f64>().unwrap();
1056            writer.write(ryu::Buffer::new().format(v).as_bytes());
1057        }
1058        Some(ScalarType::U8) => {
1059            let v = *peek.get::<u8>().unwrap();
1060            writer.write(itoa::Buffer::new().format(v).as_bytes());
1061        }
1062        Some(ScalarType::U16) => {
1063            let v = *peek.get::<u16>().unwrap();
1064            writer.write(itoa::Buffer::new().format(v).as_bytes());
1065        }
1066        Some(ScalarType::U32) => {
1067            let v = *peek.get::<u32>().unwrap();
1068            writer.write(itoa::Buffer::new().format(v).as_bytes());
1069        }
1070        Some(ScalarType::U64) => {
1071            let v = *peek.get::<u64>().unwrap();
1072            writer.write(itoa::Buffer::new().format(v).as_bytes());
1073        }
1074        Some(ScalarType::U128) => {
1075            let v = *peek.get::<u128>().unwrap();
1076            writer.write(itoa::Buffer::new().format(v).as_bytes());
1077        }
1078        Some(ScalarType::USize) => {
1079            let v = *peek.get::<usize>().unwrap();
1080            writer.write(itoa::Buffer::new().format(v).as_bytes());
1081        }
1082        Some(ScalarType::I8) => {
1083            let v = *peek.get::<i8>().unwrap();
1084            writer.write(itoa::Buffer::new().format(v).as_bytes());
1085        }
1086        Some(ScalarType::I16) => {
1087            let v = *peek.get::<i16>().unwrap();
1088            writer.write(itoa::Buffer::new().format(v).as_bytes());
1089        }
1090        Some(ScalarType::I32) => {
1091            let v = *peek.get::<i32>().unwrap();
1092            writer.write(itoa::Buffer::new().format(v).as_bytes());
1093        }
1094        Some(ScalarType::I64) => {
1095            let v = *peek.get::<i64>().unwrap();
1096            writer.write(itoa::Buffer::new().format(v).as_bytes());
1097        }
1098        Some(ScalarType::I128) => {
1099            let v = *peek.get::<i128>().unwrap();
1100            writer.write(itoa::Buffer::new().format(v).as_bytes());
1101        }
1102        Some(ScalarType::ISize) => {
1103            let v = *peek.get::<isize>().unwrap();
1104            writer.write(itoa::Buffer::new().format(v).as_bytes());
1105        }
1106        Some(unsupported) => {
1107            panic!("Unsupported scalar type: {unsupported:?}")
1108        }
1109        None => {
1110            // Try Display formatting if available
1111            if peek.shape().vtable.has_display() {
1112                crate::write_json_string(writer, &alloc::format!("{peek}"));
1113            } else {
1114                panic!("Unsupported shape (no display): {}", peek.shape())
1115            }
1116        }
1117    }
1118    Ok(())
1119}
1120
1121fn serialize_array<'mem, 'facet, W: crate::JsonWrite>(
1122    iter: facet_reflect::PeekListLikeIter<'mem, 'facet>,
1123    writer: &mut W,
1124    indent: Option<&str>,
1125    depth: usize,
1126) -> Result<(), SerializeError> {
1127    writer.write(b"[");
1128    let mut first = true;
1129    for item in iter {
1130        if !first {
1131            writer.write(b",");
1132        }
1133        first = false;
1134        write_newline(writer, indent);
1135        write_indent(writer, indent, depth + 1);
1136        serialize_value(item, None, writer, indent, depth + 1)?;
1137    }
1138    if !first {
1139        write_newline(writer, indent);
1140        write_indent(writer, indent, depth);
1141    }
1142    writer.write(b"]");
1143    Ok(())
1144}
1145
1146fn serialize_byte_array<W: crate::JsonWrite>(
1147    bytes: &[u8],
1148    writer: &mut W,
1149    indent: Option<&str>,
1150    depth: usize,
1151) -> Result<(), SerializeError> {
1152    writer.write(b"[");
1153    let mut first = true;
1154    for &byte in bytes {
1155        if !first {
1156            writer.write(b",");
1157        }
1158        first = false;
1159        write_newline(writer, indent);
1160        write_indent(writer, indent, depth + 1);
1161        writer.write(itoa::Buffer::new().format(byte).as_bytes());
1162    }
1163    if !first {
1164        write_newline(writer, indent);
1165        write_indent(writer, indent, depth);
1166    }
1167    writer.write(b"]");
1168    Ok(())
1169}
1170
1171/// Serialize enum variant content (without any wrapper/tag)
1172fn serialize_enum_content<'mem, 'facet, W: crate::JsonWrite>(
1173    peek_enum: &facet_reflect::PeekEnum<'mem, 'facet>,
1174    variant: &facet_core::Variant,
1175    writer: &mut W,
1176    indent: Option<&str>,
1177    depth: usize,
1178) -> Result<(), SerializeError> {
1179    if variant.data.fields.is_empty() {
1180        // Unit variant - serialize as variant name string for untagged
1181        // This allows distinguishing between different unit variants
1182        crate::write_json_string(writer, variant.name);
1183    } else if variant_is_newtype_like(variant) {
1184        // Newtype variant - serialize the inner value directly
1185        let fields: Vec<_> = peek_enum.fields_for_serialize().collect();
1186        let (field_item, field_peek) = fields[0];
1187        serialize_value(field_peek, Some(field_item), writer, indent, depth)?;
1188    } else if variant.data.kind == StructKind::Tuple || variant.data.kind == StructKind::TupleStruct
1189    {
1190        // Tuple variant - serialize as array
1191        writer.write(b"[");
1192        let mut first = true;
1193        for (field_item, field_peek) in peek_enum.fields_for_serialize() {
1194            if !first {
1195                writer.write(b",");
1196            }
1197            first = false;
1198            write_newline(writer, indent);
1199            write_indent(writer, indent, depth + 1);
1200            serialize_value(field_peek, Some(field_item), writer, indent, depth + 1)?;
1201        }
1202        if !first {
1203            write_newline(writer, indent);
1204            write_indent(writer, indent, depth);
1205        }
1206        writer.write(b"]");
1207    } else {
1208        // Struct variant - serialize as object
1209        writer.write(b"{");
1210        let mut first = true;
1211        for (field_item, field_peek) in peek_enum.fields_for_serialize() {
1212            if !first {
1213                writer.write(b",");
1214            }
1215            first = false;
1216            write_newline(writer, indent);
1217            write_indent(writer, indent, depth + 1);
1218            crate::write_json_string(writer, field_item.name);
1219            write_colon(writer, indent);
1220            serialize_value(field_peek, Some(field_item), writer, indent, depth + 1)?;
1221        }
1222        if !first {
1223            write_newline(writer, indent);
1224            write_indent(writer, indent, depth);
1225        }
1226        writer.write(b"}");
1227    }
1228    Ok(())
1229}