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    variant.data.kind == StructKind::Tuple && variant.data.fields.len() == 1
356}
357
358/// Write indentation for pretty printing
359fn write_indent<W: crate::JsonWrite>(writer: &mut W, indent: Option<&str>, depth: usize) {
360    if let Some(indent_str) = indent {
361        for _ in 0..depth {
362            writer.write(indent_str.as_bytes());
363        }
364    }
365}
366
367/// Write a newline for pretty printing
368fn write_newline<W: crate::JsonWrite>(writer: &mut W, indent: Option<&str>) {
369    if indent.is_some() {
370        writer.write(b"\n");
371    }
372}
373
374/// Write a space after colon for pretty printing
375fn write_colon<W: crate::JsonWrite>(writer: &mut W, indent: Option<&str>) {
376    if indent.is_some() {
377        writer.write(b": ");
378    } else {
379        writer.write(b":");
380    }
381}
382
383fn serialize_value<'mem, 'facet, W: crate::JsonWrite>(
384    peek: Peek<'mem, 'facet>,
385    maybe_field_item: Option<FieldItem>,
386    writer: &mut W,
387    indent: Option<&str>,
388    depth: usize,
389) -> Result<(), SerializeError> {
390    trace!("Serializing a value, shape is {}", peek.shape());
391
392    // Handle custom serialization (field-level proxy)
393    #[cfg(feature = "alloc")]
394    if let Some(fi) = maybe_field_item
395        && fi.field.proxy_convert_out_fn().is_some()
396    {
397        let owned_peek = peek.custom_serialization(fi.field).unwrap();
398        let old_shape = peek.shape();
399        let new_shape = owned_peek.shape();
400        trace!("{old_shape} has custom serialization, serializing as {new_shape} instead");
401        return serialize_value(owned_peek.as_peek(), None, writer, indent, depth);
402    }
403
404    // Handle container-level proxy (applies even in Vec<T>, Option<T>, etc.)
405    #[cfg(feature = "alloc")]
406    if let Ok(Some(owned_peek)) = peek.custom_serialization_from_shape() {
407        let old_shape = peek.shape();
408        let new_shape = owned_peek.shape();
409        trace!("{old_shape} has container-level proxy, serializing as {new_shape} instead");
410        return serialize_value(owned_peek.as_peek(), None, writer, indent, depth);
411    }
412
413    // Handle transparent types
414    if peek.shape().is_transparent() {
415        let old_shape = peek.shape();
416        let ps = peek.into_struct().unwrap();
417        let (field, inner_peek) = ps.fields().next().unwrap();
418        let new_shape = inner_peek.shape();
419        trace!("{old_shape} is transparent, let's serialize the inner {new_shape} instead");
420        return serialize_value(
421            inner_peek,
422            Some(FieldItem::new(field)),
423            writer,
424            indent,
425            depth,
426        );
427    }
428
429    // Handle RawJson - write raw content directly
430    if peek.shape() == RawJson::SHAPE {
431        // SAFETY: We've verified the shape matches RawJson
432        #[allow(unsafe_code)]
433        let raw = unsafe { peek.data().get::<RawJson<'static>>() };
434        writer.write(raw.as_str().as_bytes());
435        return Ok(());
436    }
437
438    trace!(
439        "Matching def={:?}, ty={:?} for shape={}",
440        peek.shape().def,
441        peek.shape().ty,
442        peek.shape()
443    );
444
445    match (peek.shape().def, peek.shape().ty) {
446        (Def::Scalar, _) => {
447            let peek = peek.innermost_peek();
448            serialize_scalar(peek, writer)?;
449        }
450        (Def::List(ld), _) => {
451            if ld.t().is_type::<u8>() && peek.shape().is_type::<Vec<u8>>() {
452                // Special case for Vec<u8> - serialize as array of numbers
453                let bytes = peek.get::<Vec<u8>>().unwrap();
454                serialize_byte_array(bytes, writer, indent, depth)?;
455            } else {
456                let peek_list = peek.into_list_like().unwrap();
457                serialize_array(peek_list.iter(), writer, indent, depth)?;
458            }
459        }
460        (Def::Array(ad), _) => {
461            if ad.t().is_type::<u8>() {
462                let bytes: Vec<u8> = peek
463                    .into_list_like()
464                    .unwrap()
465                    .iter()
466                    .map(|p| *p.get::<u8>().unwrap())
467                    .collect();
468                serialize_byte_array(&bytes, writer, indent, depth)?;
469            } else {
470                let peek_list = peek.into_list_like().unwrap();
471                serialize_array(peek_list.iter(), writer, indent, depth)?;
472            }
473        }
474        (Def::Slice(sd), _) => {
475            if sd.t().is_type::<u8>() {
476                let bytes = peek.get::<[u8]>().unwrap();
477                serialize_byte_array(bytes, writer, indent, depth)?;
478            } else {
479                let peek_list = peek.into_list_like().unwrap();
480                serialize_array(peek_list.iter(), writer, indent, depth)?;
481            }
482        }
483        (Def::Map(_), _) => {
484            let peek_map = peek.into_map().unwrap();
485            writer.write(b"{");
486            let mut first = true;
487            for (key, value) in peek_map.iter() {
488                if !first {
489                    writer.write(b",");
490                }
491                first = false;
492                write_newline(writer, indent);
493                write_indent(writer, indent, depth + 1);
494                serialize_map_key(key, writer)?;
495                write_colon(writer, indent);
496                serialize_value(value, None, writer, indent, depth + 1)?;
497            }
498            if !first {
499                write_newline(writer, indent);
500                write_indent(writer, indent, depth);
501            }
502            writer.write(b"}");
503        }
504        (Def::Set(_), _) => {
505            let peek_set = peek.into_set().unwrap();
506            writer.write(b"[");
507            let mut first = true;
508            for item in peek_set.iter() {
509                if !first {
510                    writer.write(b",");
511                }
512                first = false;
513                write_newline(writer, indent);
514                write_indent(writer, indent, depth + 1);
515                serialize_value(item, None, writer, indent, depth + 1)?;
516            }
517            if !first {
518                write_newline(writer, indent);
519                write_indent(writer, indent, depth);
520            }
521            writer.write(b"]");
522        }
523        (Def::Option(_), _) => {
524            let opt = peek.into_option().unwrap();
525            if let Some(inner_peek) = opt.value() {
526                serialize_value(inner_peek, None, writer, indent, depth)?;
527            } else {
528                writer.write(b"null");
529            }
530        }
531        (Def::Pointer(_), _) => {
532            let sp = peek.into_pointer().unwrap();
533            if let Some(inner_peek) = sp.borrow_inner() {
534                serialize_value(inner_peek, None, writer, indent, depth)?;
535            } else {
536                panic!(
537                    "Smart pointer without borrow support or with opaque pointee cannot be serialized"
538                );
539            }
540        }
541        (_, Type::User(UserType::Struct(sd))) => {
542            trace!("Serializing struct: shape={}", peek.shape());
543            trace!(
544                "  Struct details: kind={:?}, field_count={}",
545                sd.kind,
546                sd.fields.len()
547            );
548
549            match sd.kind {
550                StructKind::Unit => {
551                    writer.write(b"null");
552                }
553                StructKind::Tuple => {
554                    let peek_struct = peek.into_struct().unwrap();
555                    writer.write(b"[");
556                    let mut first = true;
557                    for (field, value) in peek_struct.fields() {
558                        if !first {
559                            writer.write(b",");
560                        }
561                        first = false;
562                        write_newline(writer, indent);
563                        write_indent(writer, indent, depth + 1);
564                        serialize_value(
565                            value,
566                            Some(FieldItem::new(field)),
567                            writer,
568                            indent,
569                            depth + 1,
570                        )?;
571                    }
572                    if !first {
573                        write_newline(writer, indent);
574                        write_indent(writer, indent, depth);
575                    }
576                    writer.write(b"]");
577                }
578                StructKind::TupleStruct => {
579                    let peek_struct = peek.into_struct().unwrap();
580                    writer.write(b"[");
581                    let mut first = true;
582                    for (field_item, value) in peek_struct.fields_for_serialize() {
583                        if !first {
584                            writer.write(b",");
585                        }
586                        first = false;
587                        write_newline(writer, indent);
588                        write_indent(writer, indent, depth + 1);
589                        serialize_value(value, Some(field_item), writer, indent, depth + 1)?;
590                    }
591                    if !first {
592                        write_newline(writer, indent);
593                        write_indent(writer, indent, depth);
594                    }
595                    writer.write(b"]");
596                }
597                StructKind::Struct => {
598                    let peek_struct = peek.into_struct().unwrap();
599                    writer.write(b"{");
600                    let mut first = true;
601                    for (field_item, value) in peek_struct.fields_for_serialize() {
602                        if !first {
603                            writer.write(b",");
604                        }
605                        first = false;
606                        write_newline(writer, indent);
607                        write_indent(writer, indent, depth + 1);
608                        crate::write_json_string(writer, field_item.name);
609                        write_colon(writer, indent);
610                        serialize_value(value, Some(field_item), writer, indent, depth + 1)?;
611                    }
612                    if !first {
613                        write_newline(writer, indent);
614                        write_indent(writer, indent, depth);
615                    }
616                    writer.write(b"}");
617                }
618            }
619        }
620        (_, Type::User(UserType::Enum(_))) => {
621            let shape = peek.shape();
622            let peek_enum = peek.into_enum().unwrap();
623            let variant = peek_enum
624                .active_variant()
625                .expect("Failed to get active variant");
626            let variant_index = peek_enum
627                .variant_index()
628                .expect("Failed to get variant index");
629            trace!("Active variant index is {variant_index}, variant is {variant:?}");
630
631            // Determine enum tagging strategy
632            let is_untagged = shape.is_untagged();
633            let tag_field = shape.get_tag_attr();
634            let content_field = shape.get_content_attr();
635
636            if is_untagged {
637                // Untagged: serialize content directly without any tag
638                serialize_enum_content(&peek_enum, variant, writer, indent, depth)?;
639            } else if let Some(tag) = tag_field {
640                if let Some(content) = content_field {
641                    // Adjacently tagged: {"tag": "Variant", "content": ...}
642                    writer.write(b"{");
643                    write_newline(writer, indent);
644                    write_indent(writer, indent, depth + 1);
645                    crate::write_json_string(writer, tag);
646                    write_colon(writer, indent);
647                    crate::write_json_string(writer, variant.name);
648
649                    // Only include content field if variant has data
650                    if !variant.data.fields.is_empty() {
651                        writer.write(b",");
652                        write_newline(writer, indent);
653                        write_indent(writer, indent, depth + 1);
654                        crate::write_json_string(writer, content);
655                        write_colon(writer, indent);
656                        serialize_enum_content(&peek_enum, variant, writer, indent, depth + 1)?;
657                    }
658
659                    write_newline(writer, indent);
660                    write_indent(writer, indent, depth);
661                    writer.write(b"}");
662                } else {
663                    // Internally tagged: {"tag": "Variant", ...fields...}
664                    writer.write(b"{");
665                    write_newline(writer, indent);
666                    write_indent(writer, indent, depth + 1);
667                    crate::write_json_string(writer, tag);
668                    write_colon(writer, indent);
669                    crate::write_json_string(writer, variant.name);
670
671                    // Add struct fields at same level as tag
672                    for (field_item, field_peek) in peek_enum.fields_for_serialize() {
673                        writer.write(b",");
674                        write_newline(writer, indent);
675                        write_indent(writer, indent, depth + 1);
676                        crate::write_json_string(writer, field_item.name);
677                        write_colon(writer, indent);
678                        serialize_value(field_peek, Some(field_item), writer, indent, depth + 1)?;
679                    }
680
681                    write_newline(writer, indent);
682                    write_indent(writer, indent, depth);
683                    writer.write(b"}");
684                }
685            } else {
686                // Externally tagged (default): {"Variant": content} or "Variant" for unit
687                let flattened = maybe_field_item.map(|fi| fi.flattened).unwrap_or_default();
688
689                if variant.data.fields.is_empty() {
690                    // Unit variant - just the name as a string
691                    crate::write_json_string(writer, variant.name);
692                } else {
693                    if !flattened {
694                        // Wrap in object with variant name as key
695                        writer.write(b"{");
696                        write_newline(writer, indent);
697                        write_indent(writer, indent, depth + 1);
698                        crate::write_json_string(writer, variant.name);
699                        write_colon(writer, indent);
700                    }
701
702                    let inner_depth = if flattened { depth } else { depth + 1 };
703                    serialize_enum_content(&peek_enum, variant, writer, indent, inner_depth)?;
704
705                    if !flattened {
706                        write_newline(writer, indent);
707                        write_indent(writer, indent, depth);
708                        writer.write(b"}");
709                    }
710                }
711            }
712        }
713        (Def::DynamicValue(_), _) => {
714            let dyn_val = peek.into_dynamic_value().unwrap();
715            serialize_dynamic_value(dyn_val, writer, indent, depth)?;
716        }
717        (_, Type::Pointer(pointer_type)) => {
718            if let Some(str_value) = peek.as_str() {
719                crate::write_json_string(writer, str_value);
720            } else if let Some(bytes) = peek.as_bytes() {
721                serialize_byte_array(bytes, writer, indent, depth)?;
722            } else if let PointerType::Function(_) = pointer_type {
723                writer.write(b"null");
724            } else {
725                let innermost = peek.innermost_peek();
726                if innermost.shape() != peek.shape() {
727                    serialize_value(innermost, None, writer, indent, depth)?;
728                } else {
729                    writer.write(b"null");
730                }
731            }
732        }
733        _ => {
734            trace!(
735                "Unhandled type: {:?}, falling back to null",
736                peek.shape().ty
737            );
738            writer.write(b"null");
739        }
740    }
741
742    Ok(())
743}
744
745/// Serialize a dynamic value (like `facet_value::Value`) to JSON
746fn serialize_dynamic_value<'mem, 'facet, W: crate::JsonWrite>(
747    dyn_val: facet_reflect::PeekDynamicValue<'mem, 'facet>,
748    writer: &mut W,
749    indent: Option<&str>,
750    depth: usize,
751) -> Result<(), SerializeError> {
752    match dyn_val.kind() {
753        DynValueKind::Null => {
754            writer.write(b"null");
755        }
756        DynValueKind::Bool => {
757            if let Some(b) = dyn_val.as_bool() {
758                writer.write(if b { b"true" } else { b"false" });
759            } else {
760                writer.write(b"null");
761            }
762        }
763        DynValueKind::Number => {
764            // Try i64 first (most common for integers), then u64 (for large unsigned), then f64
765            if let Some(n) = dyn_val.as_i64() {
766                writer.write(itoa::Buffer::new().format(n).as_bytes());
767            } else if let Some(n) = dyn_val.as_u64() {
768                writer.write(itoa::Buffer::new().format(n).as_bytes());
769            } else if let Some(n) = dyn_val.as_f64() {
770                let mut buf = ryu::Buffer::new();
771                writer.write(buf.format(n).as_bytes());
772            } else {
773                writer.write(b"null");
774            }
775        }
776        DynValueKind::String => {
777            if let Some(s) = dyn_val.as_str() {
778                crate::write_json_string(writer, s);
779            } else {
780                writer.write(b"null");
781            }
782        }
783        DynValueKind::Bytes => {
784            // Serialize bytes as an array of numbers (JSON doesn't have native bytes)
785            if let Some(bytes) = dyn_val.as_bytes() {
786                serialize_byte_array(bytes, writer, indent, depth)?;
787            } else {
788                writer.write(b"null");
789            }
790        }
791        DynValueKind::Array => {
792            let len = dyn_val.array_len().unwrap_or(0);
793            if len == 0 {
794                writer.write(b"[]");
795            } else {
796                writer.write(b"[");
797                for idx in 0..len {
798                    if idx > 0 {
799                        writer.write(b",");
800                    }
801                    write_newline(writer, indent);
802                    write_indent(writer, indent, depth + 1);
803                    if let Some(elem) = dyn_val.array_get(idx) {
804                        serialize_value(elem, None, writer, indent, depth + 1)?;
805                    } else {
806                        writer.write(b"null");
807                    }
808                }
809                write_newline(writer, indent);
810                write_indent(writer, indent, depth);
811                writer.write(b"]");
812            }
813        }
814        DynValueKind::Object => {
815            let len = dyn_val.object_len().unwrap_or(0);
816            if len == 0 {
817                writer.write(b"{}");
818            } else {
819                writer.write(b"{");
820                for idx in 0..len {
821                    if idx > 0 {
822                        writer.write(b",");
823                    }
824                    write_newline(writer, indent);
825                    write_indent(writer, indent, depth + 1);
826                    if let Some((key, val)) = dyn_val.object_get_entry(idx) {
827                        crate::write_json_string(writer, key);
828                        write_colon(writer, indent);
829                        serialize_value(val, None, writer, indent, depth + 1)?;
830                    }
831                }
832                write_newline(writer, indent);
833                write_indent(writer, indent, depth);
834                writer.write(b"}");
835            }
836        }
837        DynValueKind::DateTime => {
838            // Serialize datetime as ISO 8601 string
839            if let Some((year, month, day, hour, minute, second, nanos, kind)) =
840                dyn_val.as_datetime()
841            {
842                use facet_core::DynDateTimeKind;
843                let mut buf = String::new();
844                use core::fmt::Write;
845
846                match kind {
847                    DynDateTimeKind::LocalDate => {
848                        write!(buf, "{year:04}-{month:02}-{day:02}").unwrap();
849                    }
850                    DynDateTimeKind::LocalTime => {
851                        if nanos > 0 {
852                            write!(buf, "{hour:02}:{minute:02}:{second:02}.{nanos:09}").unwrap();
853                            // Trim trailing zeros from nanos
854                            while buf.ends_with('0') {
855                                buf.pop();
856                            }
857                        } else {
858                            write!(buf, "{hour:02}:{minute:02}:{second:02}").unwrap();
859                        }
860                    }
861                    DynDateTimeKind::LocalDateTime => {
862                        if nanos > 0 {
863                            write!(
864                                buf,
865                                "{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}.{nanos:09}"
866                            )
867                            .unwrap();
868                            while buf.ends_with('0') {
869                                buf.pop();
870                            }
871                        } else {
872                            write!(
873                                buf,
874                                "{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}"
875                            )
876                            .unwrap();
877                        }
878                    }
879                    DynDateTimeKind::Offset { offset_minutes } => {
880                        if nanos > 0 {
881                            write!(
882                                buf,
883                                "{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}.{nanos:09}"
884                            )
885                            .unwrap();
886                            while buf.ends_with('0') {
887                                buf.pop();
888                            }
889                        } else {
890                            write!(
891                                buf,
892                                "{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}"
893                            )
894                            .unwrap();
895                        }
896                        if offset_minutes == 0 {
897                            buf.push('Z');
898                        } else {
899                            let sign = if offset_minutes >= 0 { '+' } else { '-' };
900                            let abs_offset = offset_minutes.abs();
901                            let offset_hours = abs_offset / 60;
902                            let offset_mins = abs_offset % 60;
903                            write!(buf, "{sign}{offset_hours:02}:{offset_mins:02}").unwrap();
904                        }
905                    }
906                }
907                crate::write_json_string(writer, &buf);
908            } else {
909                writer.write(b"null");
910            }
911        }
912        DynValueKind::QName | DynValueKind::Uuid => {
913            // These are typically string-like; try to get a string representation
914            // For now, serialize as null since we don't have a standard getter
915            writer.write(b"null");
916        }
917    }
918    Ok(())
919}
920
921/// Serialize a map key - JSON requires object keys to be strings
922fn serialize_map_key<W: crate::JsonWrite>(
923    peek: Peek<'_, '_>,
924    writer: &mut W,
925) -> Result<(), SerializeError> {
926    // First try as_str() which handles &str, String, Cow<str>, etc uniformly
927    if let Some(s) = peek.as_str() {
928        crate::write_json_string(writer, s);
929        return Ok(());
930    }
931
932    let peek = peek.innermost_peek();
933    match peek.scalar_type() {
934        // For numeric types, convert to string representation
935        Some(ScalarType::U8) => {
936            let v = *peek.get::<u8>().unwrap();
937            writer.write(b"\"");
938            writer.write(itoa::Buffer::new().format(v).as_bytes());
939            writer.write(b"\"");
940        }
941        Some(ScalarType::U16) => {
942            let v = *peek.get::<u16>().unwrap();
943            writer.write(b"\"");
944            writer.write(itoa::Buffer::new().format(v).as_bytes());
945            writer.write(b"\"");
946        }
947        Some(ScalarType::U32) => {
948            let v = *peek.get::<u32>().unwrap();
949            writer.write(b"\"");
950            writer.write(itoa::Buffer::new().format(v).as_bytes());
951            writer.write(b"\"");
952        }
953        Some(ScalarType::U64) => {
954            let v = *peek.get::<u64>().unwrap();
955            writer.write(b"\"");
956            writer.write(itoa::Buffer::new().format(v).as_bytes());
957            writer.write(b"\"");
958        }
959        Some(ScalarType::U128) => {
960            let v = *peek.get::<u128>().unwrap();
961            writer.write(b"\"");
962            writer.write(itoa::Buffer::new().format(v).as_bytes());
963            writer.write(b"\"");
964        }
965        Some(ScalarType::USize) => {
966            let v = *peek.get::<usize>().unwrap();
967            writer.write(b"\"");
968            writer.write(itoa::Buffer::new().format(v).as_bytes());
969            writer.write(b"\"");
970        }
971        Some(ScalarType::I8) => {
972            let v = *peek.get::<i8>().unwrap();
973            writer.write(b"\"");
974            writer.write(itoa::Buffer::new().format(v).as_bytes());
975            writer.write(b"\"");
976        }
977        Some(ScalarType::I16) => {
978            let v = *peek.get::<i16>().unwrap();
979            writer.write(b"\"");
980            writer.write(itoa::Buffer::new().format(v).as_bytes());
981            writer.write(b"\"");
982        }
983        Some(ScalarType::I32) => {
984            let v = *peek.get::<i32>().unwrap();
985            writer.write(b"\"");
986            writer.write(itoa::Buffer::new().format(v).as_bytes());
987            writer.write(b"\"");
988        }
989        Some(ScalarType::I64) => {
990            let v = *peek.get::<i64>().unwrap();
991            writer.write(b"\"");
992            writer.write(itoa::Buffer::new().format(v).as_bytes());
993            writer.write(b"\"");
994        }
995        Some(ScalarType::I128) => {
996            let v = *peek.get::<i128>().unwrap();
997            writer.write(b"\"");
998            writer.write(itoa::Buffer::new().format(v).as_bytes());
999            writer.write(b"\"");
1000        }
1001        Some(ScalarType::ISize) => {
1002            let v = *peek.get::<isize>().unwrap();
1003            writer.write(b"\"");
1004            writer.write(itoa::Buffer::new().format(v).as_bytes());
1005            writer.write(b"\"");
1006        }
1007        _ => {
1008            // Fallback: use Display if available
1009            if peek.shape().vtable.has_display() {
1010                crate::write_json_string(writer, &alloc::format!("{peek}"));
1011            } else {
1012                panic!("Unsupported map key type: {}", peek.shape())
1013            }
1014        }
1015    }
1016    Ok(())
1017}
1018
1019fn serialize_scalar<W: crate::JsonWrite>(
1020    peek: Peek<'_, '_>,
1021    writer: &mut W,
1022) -> Result<(), SerializeError> {
1023    match peek.scalar_type() {
1024        Some(ScalarType::Unit) => writer.write(b"null"),
1025        Some(ScalarType::Bool) => {
1026            let v = *peek.get::<bool>().unwrap();
1027            writer.write(if v { b"true" } else { b"false" });
1028        }
1029        Some(ScalarType::Char) => {
1030            let c = *peek.get::<char>().unwrap();
1031            writer.write(b"\"");
1032            crate::write_json_escaped_char(writer, c);
1033            writer.write(b"\"");
1034        }
1035        Some(ScalarType::Str) => {
1036            crate::write_json_string(writer, peek.get::<str>().unwrap());
1037        }
1038        Some(ScalarType::String) => {
1039            crate::write_json_string(writer, peek.get::<String>().unwrap());
1040        }
1041        Some(ScalarType::CowStr) => {
1042            // SAFETY: We've verified the scalar type matches CowStr
1043            #[allow(unsafe_code)]
1044            let cow_str = unsafe { peek.data().get::<alloc::borrow::Cow<'static, str>>() };
1045            crate::write_json_string(writer, cow_str.as_ref());
1046        }
1047        Some(ScalarType::F32) => {
1048            let v = *peek.get::<f32>().unwrap();
1049            writer.write(ryu::Buffer::new().format(v).as_bytes());
1050        }
1051        Some(ScalarType::F64) => {
1052            let v = *peek.get::<f64>().unwrap();
1053            writer.write(ryu::Buffer::new().format(v).as_bytes());
1054        }
1055        Some(ScalarType::U8) => {
1056            let v = *peek.get::<u8>().unwrap();
1057            writer.write(itoa::Buffer::new().format(v).as_bytes());
1058        }
1059        Some(ScalarType::U16) => {
1060            let v = *peek.get::<u16>().unwrap();
1061            writer.write(itoa::Buffer::new().format(v).as_bytes());
1062        }
1063        Some(ScalarType::U32) => {
1064            let v = *peek.get::<u32>().unwrap();
1065            writer.write(itoa::Buffer::new().format(v).as_bytes());
1066        }
1067        Some(ScalarType::U64) => {
1068            let v = *peek.get::<u64>().unwrap();
1069            writer.write(itoa::Buffer::new().format(v).as_bytes());
1070        }
1071        Some(ScalarType::U128) => {
1072            let v = *peek.get::<u128>().unwrap();
1073            writer.write(itoa::Buffer::new().format(v).as_bytes());
1074        }
1075        Some(ScalarType::USize) => {
1076            let v = *peek.get::<usize>().unwrap();
1077            writer.write(itoa::Buffer::new().format(v).as_bytes());
1078        }
1079        Some(ScalarType::I8) => {
1080            let v = *peek.get::<i8>().unwrap();
1081            writer.write(itoa::Buffer::new().format(v).as_bytes());
1082        }
1083        Some(ScalarType::I16) => {
1084            let v = *peek.get::<i16>().unwrap();
1085            writer.write(itoa::Buffer::new().format(v).as_bytes());
1086        }
1087        Some(ScalarType::I32) => {
1088            let v = *peek.get::<i32>().unwrap();
1089            writer.write(itoa::Buffer::new().format(v).as_bytes());
1090        }
1091        Some(ScalarType::I64) => {
1092            let v = *peek.get::<i64>().unwrap();
1093            writer.write(itoa::Buffer::new().format(v).as_bytes());
1094        }
1095        Some(ScalarType::I128) => {
1096            let v = *peek.get::<i128>().unwrap();
1097            writer.write(itoa::Buffer::new().format(v).as_bytes());
1098        }
1099        Some(ScalarType::ISize) => {
1100            let v = *peek.get::<isize>().unwrap();
1101            writer.write(itoa::Buffer::new().format(v).as_bytes());
1102        }
1103        Some(unsupported) => {
1104            panic!("Unsupported scalar type: {unsupported:?}")
1105        }
1106        None => {
1107            // Try Display formatting if available
1108            if peek.shape().vtable.has_display() {
1109                crate::write_json_string(writer, &alloc::format!("{peek}"));
1110            } else {
1111                panic!("Unsupported shape (no display): {}", peek.shape())
1112            }
1113        }
1114    }
1115    Ok(())
1116}
1117
1118fn serialize_array<'mem, 'facet, W: crate::JsonWrite>(
1119    iter: facet_reflect::PeekListLikeIter<'mem, 'facet>,
1120    writer: &mut W,
1121    indent: Option<&str>,
1122    depth: usize,
1123) -> Result<(), SerializeError> {
1124    writer.write(b"[");
1125    let mut first = true;
1126    for item in iter {
1127        if !first {
1128            writer.write(b",");
1129        }
1130        first = false;
1131        write_newline(writer, indent);
1132        write_indent(writer, indent, depth + 1);
1133        serialize_value(item, None, writer, indent, depth + 1)?;
1134    }
1135    if !first {
1136        write_newline(writer, indent);
1137        write_indent(writer, indent, depth);
1138    }
1139    writer.write(b"]");
1140    Ok(())
1141}
1142
1143fn serialize_byte_array<W: crate::JsonWrite>(
1144    bytes: &[u8],
1145    writer: &mut W,
1146    indent: Option<&str>,
1147    depth: usize,
1148) -> Result<(), SerializeError> {
1149    writer.write(b"[");
1150    let mut first = true;
1151    for &byte in bytes {
1152        if !first {
1153            writer.write(b",");
1154        }
1155        first = false;
1156        write_newline(writer, indent);
1157        write_indent(writer, indent, depth + 1);
1158        writer.write(itoa::Buffer::new().format(byte).as_bytes());
1159    }
1160    if !first {
1161        write_newline(writer, indent);
1162        write_indent(writer, indent, depth);
1163    }
1164    writer.write(b"]");
1165    Ok(())
1166}
1167
1168/// Serialize enum variant content (without any wrapper/tag)
1169fn serialize_enum_content<'mem, 'facet, W: crate::JsonWrite>(
1170    peek_enum: &facet_reflect::PeekEnum<'mem, 'facet>,
1171    variant: &facet_core::Variant,
1172    writer: &mut W,
1173    indent: Option<&str>,
1174    depth: usize,
1175) -> Result<(), SerializeError> {
1176    if variant.data.fields.is_empty() {
1177        // Unit variant - serialize as variant name string for untagged
1178        // This allows distinguishing between different unit variants
1179        crate::write_json_string(writer, variant.name);
1180    } else if variant_is_newtype_like(variant) {
1181        // Newtype variant - serialize the inner value directly
1182        let fields: Vec<_> = peek_enum.fields_for_serialize().collect();
1183        let (field_item, field_peek) = fields[0];
1184        serialize_value(field_peek, Some(field_item), writer, indent, depth)?;
1185    } else if variant.data.kind == StructKind::Tuple || variant.data.kind == StructKind::TupleStruct
1186    {
1187        // Tuple variant - serialize as array
1188        writer.write(b"[");
1189        let mut first = true;
1190        for (field_item, field_peek) in peek_enum.fields_for_serialize() {
1191            if !first {
1192                writer.write(b",");
1193            }
1194            first = false;
1195            write_newline(writer, indent);
1196            write_indent(writer, indent, depth + 1);
1197            serialize_value(field_peek, Some(field_item), writer, indent, depth + 1)?;
1198        }
1199        if !first {
1200            write_newline(writer, indent);
1201            write_indent(writer, indent, depth);
1202        }
1203        writer.write(b"]");
1204    } else {
1205        // Struct variant - serialize as object
1206        writer.write(b"{");
1207        let mut first = true;
1208        for (field_item, field_peek) in peek_enum.fields_for_serialize() {
1209            if !first {
1210                writer.write(b",");
1211            }
1212            first = false;
1213            write_newline(writer, indent);
1214            write_indent(writer, indent, depth + 1);
1215            crate::write_json_string(writer, field_item.name);
1216            write_colon(writer, indent);
1217            serialize_value(field_peek, Some(field_item), writer, indent, depth + 1)?;
1218        }
1219        if !first {
1220            write_newline(writer, indent);
1221            write_indent(writer, indent, depth);
1222        }
1223        writer.write(b"}");
1224    }
1225    Ok(())
1226}