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