facet_format/
serializer.rs

1extern crate alloc;
2
3use alloc::borrow::Cow;
4use alloc::string::String;
5use core::fmt::Debug;
6use core::fmt::Write as _;
7
8use facet_core::{Def, DynDateTimeKind, DynValueKind, ScalarType, StructKind};
9use facet_reflect::{HasFields as _, Peek, ReflectError};
10
11use crate::ScalarValue;
12
13/// Field ordering preference for serialization.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
15pub enum FieldOrdering {
16    /// Fields are serialized in declaration order (default for JSON, etc.)
17    #[default]
18    Declaration,
19    /// Attributes first, then elements, then text (for XML)
20    AttributesFirst,
21}
22
23/// How struct fields should be serialized.
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
25pub enum StructFieldMode {
26    /// Serialize fields with names/keys (default for text formats).
27    #[default]
28    Named,
29    /// Serialize fields in declaration order without names (binary formats).
30    Unnamed,
31}
32
33/// How map-like values should be serialized.
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
35pub enum MapEncoding {
36    /// Serialize maps as objects/structs with string keys.
37    #[default]
38    Struct,
39    /// Serialize maps as key/value pairs (binary formats).
40    Pairs,
41}
42
43/// How enum variants should be serialized.
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
45pub enum EnumVariantEncoding {
46    /// Serialize enums using tag/field-name strategies (default for text formats).
47    #[default]
48    Tagged,
49    /// Serialize enums using a numeric variant index followed by fields (binary formats).
50    Index,
51}
52
53/// How dynamic values (e.g. `facet_value::Value`) should be encoded.
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
55pub enum DynamicValueEncoding {
56    /// Use the format's native self-describing encoding (default for JSON, MsgPack, etc.).
57    #[default]
58    SelfDescribing,
59    /// Use an explicit type tag before the dynamic value payload (binary formats).
60    Tagged,
61}
62
63/// Tag describing the concrete payload type for a dynamic value.
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum DynamicValueTag {
66    /// Null value.
67    Null,
68    /// Boolean value.
69    Bool,
70    /// Signed 64-bit integer.
71    I64,
72    /// Unsigned 64-bit integer.
73    U64,
74    /// 64-bit float.
75    F64,
76    /// UTF-8 string.
77    String,
78    /// Raw bytes.
79    Bytes,
80    /// Sequence/array.
81    Array,
82    /// Object/map.
83    Object,
84    /// Date/time value (encoded as string for tagged formats).
85    DateTime,
86}
87
88/// Low-level serializer interface implemented by each format backend.
89///
90/// This is intentionally event-ish: the shared serializer logic owns traversal
91/// (struct/enum/seq decisions), while formats own representation details.
92pub trait FormatSerializer {
93    /// Format-specific error type.
94    type Error: Debug;
95
96    /// Begin a map/object/struct.
97    fn begin_struct(&mut self) -> Result<(), Self::Error>;
98    /// Emit a field key within a struct.
99    fn field_key(&mut self, key: &str) -> Result<(), Self::Error>;
100    /// End a map/object/struct.
101    fn end_struct(&mut self) -> Result<(), Self::Error>;
102
103    /// Begin a sequence/array.
104    fn begin_seq(&mut self) -> Result<(), Self::Error>;
105    /// End a sequence/array.
106    fn end_seq(&mut self) -> Result<(), Self::Error>;
107
108    /// Emit a scalar value.
109    fn scalar(&mut self, scalar: ScalarValue<'_>) -> Result<(), Self::Error>;
110
111    /// Optional: Provide field metadata before field_key is called.
112    /// This allows formats like XML to extract namespace information.
113    /// Default implementation does nothing.
114    fn field_metadata(&mut self, _field: &facet_reflect::FieldItem) -> Result<(), Self::Error> {
115        Ok(())
116    }
117
118    /// Optional: Provide struct/enum type metadata when beginning to serialize it.
119    /// This allows formats to extract container-level attributes like xml::ns_all.
120    /// Default implementation does nothing.
121    fn struct_metadata(&mut self, _shape: &facet_core::Shape) -> Result<(), Self::Error> {
122        Ok(())
123    }
124
125    /// Optional: Provide variant metadata before serializing an enum variant.
126    /// This allows formats like XML to use the variant name as the element name
127    /// for xml::elements serialization.
128    /// Default implementation does nothing.
129    fn variant_metadata(
130        &mut self,
131        _variant: &'static facet_core::Variant,
132    ) -> Result<(), Self::Error> {
133        Ok(())
134    }
135
136    /// Preferred field ordering for this format.
137    /// Formats like XML can request attributes-first ordering to avoid buffering.
138    /// Default is declaration order.
139    fn preferred_field_order(&self) -> FieldOrdering {
140        FieldOrdering::Declaration
141    }
142
143    /// Preferred struct field mode for this format.
144    fn struct_field_mode(&self) -> StructFieldMode {
145        StructFieldMode::Named
146    }
147
148    /// Preferred map encoding for this format.
149    fn map_encoding(&self) -> MapEncoding {
150        MapEncoding::Struct
151    }
152
153    /// Preferred enum variant encoding for this format.
154    fn enum_variant_encoding(&self) -> EnumVariantEncoding {
155        EnumVariantEncoding::Tagged
156    }
157
158    /// Preferred dynamic value encoding for this format.
159    fn dynamic_value_encoding(&self) -> DynamicValueEncoding {
160        DynamicValueEncoding::SelfDescribing
161    }
162
163    /// Returns the shape of the format's raw capture type for serialization.
164    ///
165    /// When serializing a value whose shape matches this, the serializer will
166    /// extract the inner string and call [`FormatSerializer::raw_scalar`] instead of normal
167    /// serialization.
168    fn raw_serialize_shape(&self) -> Option<&'static facet_core::Shape> {
169        None
170    }
171
172    /// Emit a raw scalar value (for RawJson, etc.) without any encoding/escaping.
173    ///
174    /// The content is the format-specific raw representation that should be
175    /// output directly.
176    fn raw_scalar(&mut self, content: &str) -> Result<(), Self::Error> {
177        // Default: treat as a regular string (formats should override this)
178        self.scalar(ScalarValue::Str(Cow::Borrowed(content)))
179    }
180
181    /// Serialize an opaque scalar type with a format-specific encoding.
182    ///
183    /// Returns `Ok(true)` if handled, `Ok(false)` to fall back to standard logic.
184    fn serialize_opaque_scalar(
185        &mut self,
186        _shape: &'static facet_core::Shape,
187        _value: Peek<'_, '_>,
188    ) -> Result<bool, Self::Error> {
189        Ok(false)
190    }
191
192    /// Emit a dynamic value type tag.
193    ///
194    /// Formats that use [`DynamicValueEncoding::Tagged`] should override this.
195    /// Self-describing formats can ignore it.
196    fn dynamic_value_tag(&mut self, _tag: DynamicValueTag) -> Result<(), Self::Error> {
197        Ok(())
198    }
199
200    // ─────────────────────────────────────────────────────────────────────────
201    // Binary format support methods
202    //
203    // The following methods enable proper serialization for binary formats like
204    // postcard that need length prefixes, type-precise encoding, and explicit
205    // discriminants. All have default implementations for backward compatibility
206    // with existing text-format serializers.
207    // ─────────────────────────────────────────────────────────────────────────
208
209    /// Begin a sequence with known length.
210    ///
211    /// Binary formats (postcard, msgpack) can use this to write a length prefix
212    /// before the elements. Text formats can ignore the length and just call
213    /// `begin_seq()`.
214    ///
215    /// Default: delegates to `begin_seq()`.
216    fn begin_seq_with_len(&mut self, _len: usize) -> Result<(), Self::Error> {
217        self.begin_seq()
218    }
219
220    /// Begin serializing a map with known length.
221    ///
222    /// Default: delegates to `begin_struct()` for formats that encode maps as objects.
223    fn begin_map_with_len(&mut self, _len: usize) -> Result<(), Self::Error> {
224        self.begin_struct()
225    }
226
227    /// End a map/object/struct.
228    ///
229    /// Default: delegates to `end_struct()`.
230    fn end_map(&mut self) -> Result<(), Self::Error> {
231        self.end_struct()
232    }
233
234    /// Serialize a scalar with full type information.
235    ///
236    /// Binary formats need to encode different integer sizes differently:
237    /// - postcard: u8 as raw byte, u16+ as varint, signed use zigzag
238    /// - msgpack: different tags for different sizes
239    ///
240    /// Text formats can ignore the type and use the normalized `ScalarValue`.
241    ///
242    /// Default: normalizes to `ScalarValue` and calls `scalar()`.
243    fn typed_scalar(
244        &mut self,
245        scalar_type: ScalarType,
246        value: Peek<'_, '_>,
247    ) -> Result<(), Self::Error> {
248        // Default implementation: normalize to ScalarValue and call scalar()
249        let scalar = match scalar_type {
250            ScalarType::Unit => ScalarValue::Null,
251            ScalarType::Bool => ScalarValue::Bool(*value.get::<bool>().unwrap()),
252            ScalarType::Char => ScalarValue::Char(*value.get::<char>().unwrap()),
253            ScalarType::Str | ScalarType::String | ScalarType::CowStr => {
254                ScalarValue::Str(Cow::Borrowed(value.as_str().unwrap()))
255            }
256            ScalarType::F32 => ScalarValue::F64(*value.get::<f32>().unwrap() as f64),
257            ScalarType::F64 => ScalarValue::F64(*value.get::<f64>().unwrap()),
258            ScalarType::U8 => ScalarValue::U64(*value.get::<u8>().unwrap() as u64),
259            ScalarType::U16 => ScalarValue::U64(*value.get::<u16>().unwrap() as u64),
260            ScalarType::U32 => ScalarValue::U64(*value.get::<u32>().unwrap() as u64),
261            ScalarType::U64 => ScalarValue::U64(*value.get::<u64>().unwrap()),
262            ScalarType::U128 => {
263                let n = *value.get::<u128>().unwrap();
264                ScalarValue::Str(Cow::Owned(alloc::string::ToString::to_string(&n)))
265            }
266            ScalarType::USize => ScalarValue::U64(*value.get::<usize>().unwrap() as u64),
267            ScalarType::I8 => ScalarValue::I64(*value.get::<i8>().unwrap() as i64),
268            ScalarType::I16 => ScalarValue::I64(*value.get::<i16>().unwrap() as i64),
269            ScalarType::I32 => ScalarValue::I64(*value.get::<i32>().unwrap() as i64),
270            ScalarType::I64 => ScalarValue::I64(*value.get::<i64>().unwrap()),
271            ScalarType::I128 => {
272                let n = *value.get::<i128>().unwrap();
273                ScalarValue::Str(Cow::Owned(alloc::string::ToString::to_string(&n)))
274            }
275            ScalarType::ISize => ScalarValue::I64(*value.get::<isize>().unwrap() as i64),
276            #[cfg(feature = "net")]
277            ScalarType::IpAddr => {
278                let addr = *value.get::<core::net::IpAddr>().unwrap();
279                ScalarValue::Str(Cow::Owned(alloc::string::ToString::to_string(&addr)))
280            }
281            #[cfg(feature = "net")]
282            ScalarType::Ipv4Addr => {
283                let addr = *value.get::<core::net::Ipv4Addr>().unwrap();
284                ScalarValue::Str(Cow::Owned(alloc::string::ToString::to_string(&addr)))
285            }
286            #[cfg(feature = "net")]
287            ScalarType::Ipv6Addr => {
288                let addr = *value.get::<core::net::Ipv6Addr>().unwrap();
289                ScalarValue::Str(Cow::Owned(alloc::string::ToString::to_string(&addr)))
290            }
291            #[cfg(feature = "net")]
292            ScalarType::SocketAddr => {
293                let addr = *value.get::<core::net::SocketAddr>().unwrap();
294                ScalarValue::Str(Cow::Owned(alloc::string::ToString::to_string(&addr)))
295            }
296            _ => {
297                // For unknown scalar types, try to get a string representation
298                if let Some(s) = value.as_str() {
299                    ScalarValue::Str(Cow::Borrowed(s))
300                } else {
301                    ScalarValue::Null
302                }
303            }
304        };
305        self.scalar(scalar)
306    }
307
308    /// Begin serializing `Option::Some(value)`.
309    ///
310    /// Binary formats like postcard write a `0x01` discriminant byte here.
311    /// Text formats typically don't need a prefix (they just serialize the value).
312    ///
313    /// Default: no-op (text formats).
314    fn begin_option_some(&mut self) -> Result<(), Self::Error> {
315        Ok(())
316    }
317
318    /// Serialize `Option::None`.
319    ///
320    /// Binary formats like postcard write a `0x00` discriminant byte.
321    /// Text formats typically emit `null`.
322    ///
323    /// Default: emits `ScalarValue::Null`.
324    fn serialize_none(&mut self) -> Result<(), Self::Error> {
325        self.scalar(ScalarValue::Null)
326    }
327
328    /// Begin an enum variant with its index and name.
329    ///
330    /// Binary formats like postcard write the variant index as a varint.
331    /// Text formats typically use the variant name as a key or value.
332    ///
333    /// This is called for externally tagged enums before the variant payload.
334    /// For untagged enums, this is not called.
335    ///
336    /// Default: no-op (text formats handle variants via field_key/scalar).
337    fn begin_enum_variant(
338        &mut self,
339        _variant_index: usize,
340        _variant_name: &'static str,
341    ) -> Result<(), Self::Error> {
342        Ok(())
343    }
344}
345
346/// Error produced by the shared serializer.
347#[derive(Debug)]
348pub enum SerializeError<E: Debug> {
349    /// Format backend error.
350    Backend(E),
351    /// Reflection failed while traversing the value.
352    Reflect(ReflectError),
353    /// Value can't be represented by the shared serializer.
354    Unsupported(Cow<'static, str>),
355    /// Internal invariant violation.
356    Internal(Cow<'static, str>),
357}
358
359impl<E: Debug> core::fmt::Display for SerializeError<E> {
360    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
361        match self {
362            SerializeError::Backend(_) => f.write_str("format serializer error"),
363            SerializeError::Reflect(err) => write!(f, "{err}"),
364            SerializeError::Unsupported(msg) => f.write_str(msg.as_ref()),
365            SerializeError::Internal(msg) => f.write_str(msg.as_ref()),
366        }
367    }
368}
369
370impl<E: Debug> std::error::Error for SerializeError<E> {}
371
372/// Serialize a root value using the shared traversal logic.
373pub fn serialize_root<'mem, 'facet, S>(
374    serializer: &mut S,
375    value: Peek<'mem, 'facet>,
376) -> Result<(), SerializeError<S::Error>>
377where
378    S: FormatSerializer,
379{
380    shared_serialize(serializer, value)
381}
382
383/// Helper to sort fields according to format preference
384fn sort_fields_if_needed<'mem, 'facet, S>(
385    serializer: &S,
386    fields: &mut alloc::vec::Vec<(facet_reflect::FieldItem, Peek<'mem, 'facet>)>,
387) where
388    S: FormatSerializer,
389{
390    if serializer.preferred_field_order() == FieldOrdering::AttributesFirst {
391        fields.sort_by_key(|(field_item, _)| {
392            // Determine field category: 0=attribute, 1=element, 2=text
393            // For flattened map entries (field is None), treat as attributes
394            match &field_item.field {
395                Some(field) if field.is_attribute() => 0, // attributes first
396                Some(field) if field.is_text() => 2,      // text last
397                None => 0,                                // flattened map entries are attributes
398                _ => 1,                                   // elements in the middle
399            }
400        });
401    }
402}
403
404fn shared_serialize<'mem, 'facet, S>(
405    serializer: &mut S,
406    value: Peek<'mem, 'facet>,
407) -> Result<(), SerializeError<S::Error>>
408where
409    S: FormatSerializer,
410{
411    // Dereference pointers (Box, Arc, etc.) to get the underlying value
412    let value = deref_if_pointer(value);
413
414    // Check for raw serialization type (e.g., RawJson) BEFORE innermost_peek
415    // because innermost_peek might unwrap the type if it has .inner set
416    if serializer.raw_serialize_shape() == Some(value.shape()) {
417        // RawJson is a tuple struct with a single Cow<str> field
418        // Get the inner Cow<str> value via as_str on the inner
419        if let Ok(struct_) = value.into_struct()
420            && let Some((_field_item, inner_value)) = struct_.fields_for_serialize().next()
421            && let Some(s) = inner_value.as_str()
422        {
423            return serializer.raw_scalar(s).map_err(SerializeError::Backend);
424        }
425        // If we get here, the raw shape matched but extraction failed
426        // This shouldn't happen for properly implemented raw types
427        return Err(SerializeError::Unsupported(Cow::Borrowed(
428            "raw capture type matched but could not extract inner string",
429        )));
430    }
431
432    if serializer
433        .serialize_opaque_scalar(value.shape(), value)
434        .map_err(SerializeError::Backend)?
435    {
436        return Ok(());
437    }
438
439    let value = value.innermost_peek();
440
441    // Check for container-level proxy - serialize through the proxy type
442    if let Some(proxy_def) = value.shape().proxy {
443        return serialize_via_proxy(serializer, value, proxy_def);
444    }
445
446    // Use typed_scalar for scalars - allows binary formats to encode precisely
447    if let Some(scalar_type) = value.scalar_type() {
448        return serializer
449            .typed_scalar(scalar_type, value)
450            .map_err(SerializeError::Backend);
451    }
452
453    // Fallback for Def::Scalar types with Display trait (e.g., SmolStr, SmartString, CompactString)
454    // These are string-like types that should serialize as strings
455    if matches!(value.shape().def, Def::Scalar) && value.shape().vtable.has_display() {
456        use alloc::string::ToString;
457        let formatted = value.to_string();
458        return serializer
459            .scalar(ScalarValue::Str(Cow::Owned(formatted)))
460            .map_err(SerializeError::Backend);
461    }
462
463    // Handle Option<T> - use begin_option_some/serialize_none for binary formats
464    if let Ok(opt) = value.into_option() {
465        return match opt.value() {
466            Some(inner) => {
467                serializer
468                    .begin_option_some()
469                    .map_err(SerializeError::Backend)?;
470                shared_serialize(serializer, inner)
471            }
472            None => serializer.serialize_none().map_err(SerializeError::Backend),
473        };
474    }
475
476    if let Ok(result) = value.into_result() {
477        let (variant_index, variant_name, inner) = if result.is_ok() {
478            (
479                0,
480                "Ok",
481                result.ok().ok_or(SerializeError::Internal(Cow::Borrowed(
482                    "result reported Ok but value was missing",
483                )))?,
484            )
485        } else {
486            (
487                1,
488                "Err",
489                result.err().ok_or(SerializeError::Internal(Cow::Borrowed(
490                    "result reported Err but value was missing",
491                )))?,
492            )
493        };
494
495        if serializer.enum_variant_encoding() == EnumVariantEncoding::Index {
496            serializer
497                .begin_enum_variant(variant_index, variant_name)
498                .map_err(SerializeError::Backend)?;
499            return shared_serialize(serializer, inner);
500        }
501
502        // Externally tagged representation for non-index encodings.
503        serializer.begin_struct().map_err(SerializeError::Backend)?;
504        serializer
505            .field_key(variant_name)
506            .map_err(SerializeError::Backend)?;
507        shared_serialize(serializer, inner)?;
508        serializer.end_struct().map_err(SerializeError::Backend)?;
509        return Ok(());
510    }
511
512    if let Ok(dynamic) = value.into_dynamic_value() {
513        return serialize_dynamic_value(serializer, dynamic);
514    }
515
516    match value.shape().def {
517        facet_core::Def::List(_) | facet_core::Def::Array(_) | facet_core::Def::Slice(_) => {
518            let list = value.into_list_like().map_err(SerializeError::Reflect)?;
519            let len = list.len();
520            match value.shape().def {
521                facet_core::Def::Array(_) => {
522                    serializer.begin_seq().map_err(SerializeError::Backend)?
523                }
524                _ => serializer
525                    .begin_seq_with_len(len)
526                    .map_err(SerializeError::Backend)?,
527            };
528            for item in list.iter() {
529                shared_serialize(serializer, item)?;
530            }
531            serializer.end_seq().map_err(SerializeError::Backend)?;
532            return Ok(());
533        }
534        _ => {}
535    }
536
537    if let Ok(map) = value.into_map() {
538        let len = map.len();
539        match serializer.map_encoding() {
540            MapEncoding::Pairs => {
541                serializer
542                    .begin_map_with_len(len)
543                    .map_err(SerializeError::Backend)?;
544                for (key, val) in map.iter() {
545                    shared_serialize(serializer, key)?;
546                    shared_serialize(serializer, val)?;
547                }
548                serializer.end_map().map_err(SerializeError::Backend)?;
549            }
550            MapEncoding::Struct => {
551                serializer.begin_struct().map_err(SerializeError::Backend)?;
552                for (key, val) in map.iter() {
553                    // Convert the key to a string for the field name
554                    let key_str = if let Some(s) = key.as_str() {
555                        Cow::Borrowed(s)
556                    } else {
557                        // For non-string keys, use Display format (not Debug, which adds quotes)
558                        Cow::Owned(alloc::format!("{}", key))
559                    };
560                    serializer
561                        .field_key(&key_str)
562                        .map_err(SerializeError::Backend)?;
563                    shared_serialize(serializer, val)?;
564                }
565                serializer.end_struct().map_err(SerializeError::Backend)?;
566            }
567        }
568        return Ok(());
569    }
570
571    if let Ok(set) = value.into_set() {
572        // Use begin_seq_with_len for binary formats that need length prefixes
573        let len = set.len();
574        serializer
575            .begin_seq_with_len(len)
576            .map_err(SerializeError::Backend)?;
577        for item in set.iter() {
578            shared_serialize(serializer, item)?;
579        }
580        serializer.end_seq().map_err(SerializeError::Backend)?;
581        return Ok(());
582    }
583
584    if let Ok(struct_) = value.into_struct() {
585        let kind = struct_.ty().kind;
586        if kind == StructKind::Tuple || kind == StructKind::TupleStruct {
587            // Serialize tuples as arrays without length prefixes
588            let fields: alloc::vec::Vec<_> = struct_.fields_for_serialize().collect();
589            serializer.begin_seq().map_err(SerializeError::Backend)?;
590            for (field_item, field_value) in fields {
591                // Check for field-level proxy
592                if let Some(proxy_def) = field_item.field.and_then(|f| f.proxy()) {
593                    serialize_via_proxy(serializer, field_value, proxy_def)?;
594                } else {
595                    shared_serialize(serializer, field_value)?;
596                }
597            }
598            serializer.end_seq().map_err(SerializeError::Backend)?;
599        } else {
600            // Regular structs as objects
601            serializer
602                .struct_metadata(value.shape())
603                .map_err(SerializeError::Backend)?;
604            serializer.begin_struct().map_err(SerializeError::Backend)?;
605
606            // Collect fields and sort according to format preference
607            let mut fields: alloc::vec::Vec<_> = struct_.fields_for_serialize().collect();
608            sort_fields_if_needed(serializer, &mut fields);
609            let field_mode = serializer.struct_field_mode();
610
611            for (field_item, field_value) in fields {
612                serializer
613                    .field_metadata(&field_item)
614                    .map_err(SerializeError::Backend)?;
615                if field_mode == StructFieldMode::Named {
616                    serializer
617                        .field_key(&field_item.name)
618                        .map_err(SerializeError::Backend)?;
619                }
620                // Check for field-level proxy
621                if let Some(proxy_def) = field_item.field.and_then(|f| f.proxy()) {
622                    serialize_via_proxy(serializer, field_value, proxy_def)?;
623                } else {
624                    shared_serialize(serializer, field_value)?;
625                }
626            }
627            serializer.end_struct().map_err(SerializeError::Backend)?;
628        }
629        return Ok(());
630    }
631
632    if let Ok(enum_) = value.into_enum() {
633        let variant = enum_.active_variant().map_err(|_| {
634            SerializeError::Unsupported(Cow::Borrowed("opaque enum layout is unsupported"))
635        })?;
636
637        // Notify format of the variant being serialized (for xml::elements support)
638        serializer
639            .variant_metadata(variant)
640            .map_err(SerializeError::Backend)?;
641
642        if serializer.enum_variant_encoding() == EnumVariantEncoding::Index {
643            let variant_index = enum_.variant_index().map_err(|_| {
644                SerializeError::Unsupported(Cow::Borrowed("opaque enum layout is unsupported"))
645            })?;
646            serializer
647                .begin_enum_variant(variant_index, variant.name)
648                .map_err(SerializeError::Backend)?;
649
650            match variant.data.kind {
651                StructKind::Unit => return Ok(()),
652                StructKind::TupleStruct | StructKind::Tuple | StructKind::Struct => {
653                    for (field_item, field_value) in enum_.fields_for_serialize() {
654                        if let Some(proxy_def) = field_item.field.and_then(|f| f.proxy()) {
655                            serialize_via_proxy(serializer, field_value, proxy_def)?;
656                        } else {
657                            shared_serialize(serializer, field_value)?;
658                        }
659                    }
660                    return Ok(());
661                }
662            }
663        }
664
665        let numeric = value.shape().is_numeric();
666        let untagged = value.shape().is_untagged();
667        let tag = value.shape().get_tag_attr();
668        let content = value.shape().get_content_attr();
669
670        if numeric {
671            return serialize_numeric_enum(serializer, variant);
672        }
673        if untagged {
674            return serialize_untagged_enum(serializer, enum_, variant);
675        }
676
677        match (tag, content) {
678            (Some(tag_key), None) => {
679                // Internally tagged.
680                serializer.begin_struct().map_err(SerializeError::Backend)?;
681                serializer
682                    .field_key(tag_key)
683                    .map_err(SerializeError::Backend)?;
684                serializer
685                    .scalar(ScalarValue::Str(Cow::Borrowed(variant.name)))
686                    .map_err(SerializeError::Backend)?;
687
688                match variant.data.kind {
689                    StructKind::Unit => {}
690                    StructKind::Struct => {
691                        let mut fields: alloc::vec::Vec<_> = enum_.fields_for_serialize().collect();
692                        sort_fields_if_needed(serializer, &mut fields);
693                        let field_mode = serializer.struct_field_mode();
694                        for (field_item, field_value) in fields {
695                            serializer
696                                .field_metadata(&field_item)
697                                .map_err(SerializeError::Backend)?;
698                            if field_mode == StructFieldMode::Named {
699                                serializer
700                                    .field_key(&field_item.name)
701                                    .map_err(SerializeError::Backend)?;
702                            }
703                            // Check for field-level proxy
704                            if let Some(proxy_def) = field_item.field.and_then(|f| f.proxy()) {
705                                serialize_via_proxy(serializer, field_value, proxy_def)?;
706                            } else {
707                                shared_serialize(serializer, field_value)?;
708                            }
709                        }
710                    }
711                    StructKind::TupleStruct | StructKind::Tuple => {
712                        return Err(SerializeError::Unsupported(Cow::Borrowed(
713                            "internally tagged tuple variants are not supported",
714                        )));
715                    }
716                }
717
718                serializer.end_struct().map_err(SerializeError::Backend)?;
719                return Ok(());
720            }
721            (Some(tag_key), Some(content_key)) => {
722                // Adjacently tagged.
723                serializer.begin_struct().map_err(SerializeError::Backend)?;
724                serializer
725                    .field_key(tag_key)
726                    .map_err(SerializeError::Backend)?;
727                serializer
728                    .scalar(ScalarValue::Str(Cow::Borrowed(variant.name)))
729                    .map_err(SerializeError::Backend)?;
730
731                match variant.data.kind {
732                    StructKind::Unit => {
733                        // Unit variants with adjacent tagging omit the content field.
734                    }
735                    StructKind::Struct => {
736                        serializer
737                            .field_key(content_key)
738                            .map_err(SerializeError::Backend)?;
739                        serializer.begin_struct().map_err(SerializeError::Backend)?;
740                        let mut fields: alloc::vec::Vec<_> = enum_.fields_for_serialize().collect();
741                        sort_fields_if_needed(serializer, &mut fields);
742                        let field_mode = serializer.struct_field_mode();
743                        for (field_item, field_value) in fields {
744                            serializer
745                                .field_metadata(&field_item)
746                                .map_err(SerializeError::Backend)?;
747                            if field_mode == StructFieldMode::Named {
748                                serializer
749                                    .field_key(&field_item.name)
750                                    .map_err(SerializeError::Backend)?;
751                            }
752                            // Check for field-level proxy
753                            if let Some(proxy_def) = field_item.field.and_then(|f| f.proxy()) {
754                                serialize_via_proxy(serializer, field_value, proxy_def)?;
755                            } else {
756                                shared_serialize(serializer, field_value)?;
757                            }
758                        }
759                        serializer.end_struct().map_err(SerializeError::Backend)?;
760                    }
761                    StructKind::TupleStruct | StructKind::Tuple => {
762                        serializer
763                            .field_key(content_key)
764                            .map_err(SerializeError::Backend)?;
765
766                        let field_count = variant.data.fields.len();
767                        if field_count == 1 {
768                            let inner = enum_
769                                .field(0)
770                                .map_err(|_| {
771                                    SerializeError::Internal(Cow::Borrowed(
772                                        "variant field lookup failed",
773                                    ))
774                                })?
775                                .ok_or(SerializeError::Internal(Cow::Borrowed(
776                                    "variant reported 1 field but field(0) returned None",
777                                )))?;
778                            shared_serialize(serializer, inner)?;
779                        } else {
780                            serializer.begin_seq().map_err(SerializeError::Backend)?;
781                            for idx in 0..field_count {
782                                let inner = enum_
783                                    .field(idx)
784                                    .map_err(|_| {
785                                        SerializeError::Internal(Cow::Borrowed(
786                                            "variant field lookup failed",
787                                        ))
788                                    })?
789                                    .ok_or(SerializeError::Internal(Cow::Borrowed(
790                                        "variant field missing while iterating tuple fields",
791                                    )))?;
792                                shared_serialize(serializer, inner)?;
793                            }
794                            serializer.end_seq().map_err(SerializeError::Backend)?;
795                        }
796                    }
797                }
798
799                serializer.end_struct().map_err(SerializeError::Backend)?;
800                return Ok(());
801            }
802            (None, Some(_)) => {
803                return Err(SerializeError::Unsupported(Cow::Borrowed(
804                    "adjacent content key set without tag key",
805                )));
806            }
807            (None, None) => {}
808        }
809
810        // Externally tagged (default).
811        return match variant.data.kind {
812            StructKind::Unit => {
813                serializer
814                    .scalar(ScalarValue::Str(Cow::Borrowed(variant.name)))
815                    .map_err(SerializeError::Backend)?;
816                Ok(())
817            }
818            StructKind::TupleStruct | StructKind::Tuple => {
819                serializer.begin_struct().map_err(SerializeError::Backend)?;
820                serializer
821                    .field_key(variant.name)
822                    .map_err(SerializeError::Backend)?;
823
824                let field_count = variant.data.fields.len();
825                if field_count == 1 {
826                    let inner = enum_
827                        .field(0)
828                        .map_err(|_| {
829                            SerializeError::Internal(Cow::Borrowed("variant field lookup failed"))
830                        })?
831                        .ok_or(SerializeError::Internal(Cow::Borrowed(
832                            "variant reported 1 field but field(0) returned None",
833                        )))?;
834                    shared_serialize(serializer, inner)?;
835                } else {
836                    serializer.begin_seq().map_err(SerializeError::Backend)?;
837                    for idx in 0..field_count {
838                        let inner = enum_
839                            .field(idx)
840                            .map_err(|_| {
841                                SerializeError::Internal(Cow::Borrowed(
842                                    "variant field lookup failed",
843                                ))
844                            })?
845                            .ok_or(SerializeError::Internal(Cow::Borrowed(
846                                "variant field missing while iterating tuple fields",
847                            )))?;
848                        shared_serialize(serializer, inner)?;
849                    }
850                    serializer.end_seq().map_err(SerializeError::Backend)?;
851                }
852
853                serializer.end_struct().map_err(SerializeError::Backend)?;
854                Ok(())
855            }
856            StructKind::Struct => {
857                serializer.begin_struct().map_err(SerializeError::Backend)?;
858                serializer
859                    .field_key(variant.name)
860                    .map_err(SerializeError::Backend)?;
861
862                serializer.begin_struct().map_err(SerializeError::Backend)?;
863                let mut fields: alloc::vec::Vec<_> = enum_.fields_for_serialize().collect();
864                sort_fields_if_needed(serializer, &mut fields);
865                let field_mode = serializer.struct_field_mode();
866                for (field_item, field_value) in fields {
867                    serializer
868                        .field_metadata(&field_item)
869                        .map_err(SerializeError::Backend)?;
870                    if field_mode == StructFieldMode::Named {
871                        serializer
872                            .field_key(&field_item.name)
873                            .map_err(SerializeError::Backend)?;
874                    }
875                    // Check for field-level proxy
876                    if let Some(proxy_def) = field_item.field.and_then(|f| f.proxy()) {
877                        serialize_via_proxy(serializer, field_value, proxy_def)?;
878                    } else {
879                        shared_serialize(serializer, field_value)?;
880                    }
881                }
882                serializer.end_struct().map_err(SerializeError::Backend)?;
883
884                serializer.end_struct().map_err(SerializeError::Backend)?;
885                Ok(())
886            }
887        };
888    }
889
890    Err(SerializeError::Unsupported(Cow::Borrowed(
891        "unsupported value kind for serialization",
892    )))
893}
894
895fn serialize_dynamic_value<'mem, 'facet, S>(
896    serializer: &mut S,
897    dynamic: facet_reflect::PeekDynamicValue<'mem, 'facet>,
898) -> Result<(), SerializeError<S::Error>>
899where
900    S: FormatSerializer,
901{
902    let tagged = serializer.dynamic_value_encoding() == DynamicValueEncoding::Tagged;
903
904    match dynamic.kind() {
905        DynValueKind::Null => {
906            if tagged {
907                serializer
908                    .dynamic_value_tag(DynamicValueTag::Null)
909                    .map_err(SerializeError::Backend)?;
910            }
911            serializer
912                .scalar(ScalarValue::Null)
913                .map_err(SerializeError::Backend)
914        }
915        DynValueKind::Bool => {
916            let value = dynamic.as_bool().ok_or_else(|| {
917                SerializeError::Internal(Cow::Borrowed("dynamic bool missing value"))
918            })?;
919            if tagged {
920                serializer
921                    .dynamic_value_tag(DynamicValueTag::Bool)
922                    .map_err(SerializeError::Backend)?;
923            }
924            serializer
925                .scalar(ScalarValue::Bool(value))
926                .map_err(SerializeError::Backend)
927        }
928        DynValueKind::Number => {
929            if let Some(n) = dynamic.as_i64() {
930                if tagged {
931                    serializer
932                        .dynamic_value_tag(DynamicValueTag::I64)
933                        .map_err(SerializeError::Backend)?;
934                }
935                serializer
936                    .scalar(ScalarValue::I64(n))
937                    .map_err(SerializeError::Backend)
938            } else if let Some(n) = dynamic.as_u64() {
939                if tagged {
940                    serializer
941                        .dynamic_value_tag(DynamicValueTag::U64)
942                        .map_err(SerializeError::Backend)?;
943                }
944                serializer
945                    .scalar(ScalarValue::U64(n))
946                    .map_err(SerializeError::Backend)
947            } else if let Some(n) = dynamic.as_f64() {
948                if tagged {
949                    serializer
950                        .dynamic_value_tag(DynamicValueTag::F64)
951                        .map_err(SerializeError::Backend)?;
952                }
953                serializer
954                    .scalar(ScalarValue::F64(n))
955                    .map_err(SerializeError::Backend)
956            } else {
957                Err(SerializeError::Unsupported(Cow::Borrowed(
958                    "dynamic number not representable",
959                )))
960            }
961        }
962        DynValueKind::String => {
963            let value = dynamic.as_str().ok_or_else(|| {
964                SerializeError::Internal(Cow::Borrowed("dynamic string missing value"))
965            })?;
966            if tagged {
967                serializer
968                    .dynamic_value_tag(DynamicValueTag::String)
969                    .map_err(SerializeError::Backend)?;
970            }
971            serializer
972                .scalar(ScalarValue::Str(Cow::Borrowed(value)))
973                .map_err(SerializeError::Backend)
974        }
975        DynValueKind::Bytes => {
976            let value = dynamic.as_bytes().ok_or_else(|| {
977                SerializeError::Internal(Cow::Borrowed("dynamic bytes missing value"))
978            })?;
979            if tagged {
980                serializer
981                    .dynamic_value_tag(DynamicValueTag::Bytes)
982                    .map_err(SerializeError::Backend)?;
983            }
984            serializer
985                .scalar(ScalarValue::Bytes(Cow::Borrowed(value)))
986                .map_err(SerializeError::Backend)
987        }
988        DynValueKind::Array => {
989            let len = dynamic.array_len().ok_or_else(|| {
990                SerializeError::Internal(Cow::Borrowed("dynamic array missing length"))
991            })?;
992            if tagged {
993                serializer
994                    .dynamic_value_tag(DynamicValueTag::Array)
995                    .map_err(SerializeError::Backend)?;
996            }
997            serializer
998                .begin_seq_with_len(len)
999                .map_err(SerializeError::Backend)?;
1000            if let Some(iter) = dynamic.array_iter() {
1001                for item in iter {
1002                    shared_serialize(serializer, item)?;
1003                }
1004            }
1005            serializer.end_seq().map_err(SerializeError::Backend)
1006        }
1007        DynValueKind::Object => {
1008            let len = dynamic.object_len().ok_or_else(|| {
1009                SerializeError::Internal(Cow::Borrowed("dynamic object missing length"))
1010            })?;
1011            if tagged {
1012                serializer
1013                    .dynamic_value_tag(DynamicValueTag::Object)
1014                    .map_err(SerializeError::Backend)?;
1015            }
1016            match serializer.map_encoding() {
1017                MapEncoding::Pairs => {
1018                    serializer
1019                        .begin_map_with_len(len)
1020                        .map_err(SerializeError::Backend)?;
1021                    if let Some(iter) = dynamic.object_iter() {
1022                        for (key, value) in iter {
1023                            serializer
1024                                .scalar(ScalarValue::Str(Cow::Borrowed(key)))
1025                                .map_err(SerializeError::Backend)?;
1026                            shared_serialize(serializer, value)?;
1027                        }
1028                    }
1029                    serializer.end_map().map_err(SerializeError::Backend)
1030                }
1031                MapEncoding::Struct => {
1032                    serializer.begin_struct().map_err(SerializeError::Backend)?;
1033                    if let Some(iter) = dynamic.object_iter() {
1034                        for (key, value) in iter {
1035                            serializer.field_key(key).map_err(SerializeError::Backend)?;
1036                            shared_serialize(serializer, value)?;
1037                        }
1038                    }
1039                    serializer.end_struct().map_err(SerializeError::Backend)
1040                }
1041            }
1042        }
1043        DynValueKind::DateTime => {
1044            let dt = dynamic.as_datetime().ok_or_else(|| {
1045                SerializeError::Internal(Cow::Borrowed("dynamic datetime missing value"))
1046            })?;
1047            if tagged {
1048                serializer
1049                    .dynamic_value_tag(DynamicValueTag::DateTime)
1050                    .map_err(SerializeError::Backend)?;
1051            }
1052            let s = format_dyn_datetime(dt);
1053            serializer
1054                .scalar(ScalarValue::Str(Cow::Owned(s)))
1055                .map_err(SerializeError::Backend)
1056        }
1057        DynValueKind::QName | DynValueKind::Uuid => Err(SerializeError::Unsupported(
1058            Cow::Borrowed("dynamic QName/Uuid serialization is not supported"),
1059        )),
1060    }
1061}
1062
1063fn format_dyn_datetime(
1064    (year, month, day, hour, minute, second, nanos, kind): (
1065        i32,
1066        u8,
1067        u8,
1068        u8,
1069        u8,
1070        u8,
1071        u32,
1072        DynDateTimeKind,
1073    ),
1074) -> String {
1075    let mut out = String::new();
1076    match kind {
1077        DynDateTimeKind::Offset { offset_minutes } => {
1078            let _ = write!(
1079                out,
1080                "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}",
1081                year, month, day, hour, minute, second
1082            );
1083            if nanos > 0 {
1084                let _ = write!(out, ".{:09}", nanos);
1085            }
1086            if offset_minutes == 0 {
1087                out.push('Z');
1088            } else {
1089                let sign = if offset_minutes >= 0 { '+' } else { '-' };
1090                let abs = offset_minutes.unsigned_abs();
1091                let _ = write!(out, "{}{:02}:{:02}", sign, abs / 60, abs % 60);
1092            }
1093        }
1094        DynDateTimeKind::LocalDateTime => {
1095            let _ = write!(
1096                out,
1097                "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}",
1098                year, month, day, hour, minute, second
1099            );
1100            if nanos > 0 {
1101                let _ = write!(out, ".{:09}", nanos);
1102            }
1103        }
1104        DynDateTimeKind::LocalDate => {
1105            let _ = write!(out, "{:04}-{:02}-{:02}", year, month, day);
1106        }
1107        DynDateTimeKind::LocalTime => {
1108            let _ = write!(out, "{:02}:{:02}:{:02}", hour, minute, second);
1109            if nanos > 0 {
1110                let _ = write!(out, ".{:09}", nanos);
1111            }
1112        }
1113    }
1114    out
1115}
1116
1117fn serialize_numeric_enum<S>(
1118    serializer: &mut S,
1119    variant: &'static facet_core::Variant,
1120) -> Result<(), SerializeError<S::Error>>
1121where
1122    S: FormatSerializer,
1123{
1124    let discriminant = variant
1125        .discriminant
1126        .ok_or(SerializeError::Unsupported(Cow::Borrowed(
1127            "Enum without a discriminant",
1128        )))?;
1129    serializer
1130        .scalar(ScalarValue::I64(discriminant))
1131        .map_err(SerializeError::Backend)
1132}
1133
1134fn serialize_untagged_enum<'mem, 'facet, S>(
1135    serializer: &mut S,
1136    enum_: facet_reflect::PeekEnum<'mem, 'facet>,
1137    variant: &'static facet_core::Variant,
1138) -> Result<(), SerializeError<S::Error>>
1139where
1140    S: FormatSerializer,
1141{
1142    match variant.data.kind {
1143        StructKind::Unit => {
1144            // The codex test suite uses `null` for unit variants like `Null`.
1145            // To preserve round-trippability for those fixtures, treat a `Null`
1146            // variant name specially; other unit variants fall back to a string.
1147            if variant.name.eq_ignore_ascii_case("null") {
1148                return serializer
1149                    .scalar(ScalarValue::Null)
1150                    .map_err(SerializeError::Backend);
1151            }
1152            serializer
1153                .scalar(ScalarValue::Str(Cow::Borrowed(variant.name)))
1154                .map_err(SerializeError::Backend)
1155        }
1156        StructKind::TupleStruct | StructKind::Tuple => {
1157            let field_count = variant.data.fields.len();
1158            if field_count == 1 {
1159                let inner = enum_
1160                    .field(0)
1161                    .map_err(|_| {
1162                        SerializeError::Internal(Cow::Borrowed("variant field lookup failed"))
1163                    })?
1164                    .ok_or(SerializeError::Internal(Cow::Borrowed(
1165                        "variant reported 1 field but field(0) returned None",
1166                    )))?;
1167                shared_serialize(serializer, inner)
1168            } else {
1169                serializer.begin_seq().map_err(SerializeError::Backend)?;
1170                for idx in 0..field_count {
1171                    let inner = enum_
1172                        .field(idx)
1173                        .map_err(|_| {
1174                            SerializeError::Internal(Cow::Borrowed("variant field lookup failed"))
1175                        })?
1176                        .ok_or(SerializeError::Internal(Cow::Borrowed(
1177                            "variant field missing while iterating tuple fields",
1178                        )))?;
1179                    shared_serialize(serializer, inner)?;
1180                }
1181                serializer.end_seq().map_err(SerializeError::Backend)?;
1182                Ok(())
1183            }
1184        }
1185        StructKind::Struct => {
1186            serializer.begin_struct().map_err(SerializeError::Backend)?;
1187            let mut fields: alloc::vec::Vec<_> = enum_.fields_for_serialize().collect();
1188            sort_fields_if_needed(serializer, &mut fields);
1189            for (field_item, field_value) in fields {
1190                serializer
1191                    .field_metadata(&field_item)
1192                    .map_err(SerializeError::Backend)?;
1193                serializer
1194                    .field_key(&field_item.name)
1195                    .map_err(SerializeError::Backend)?;
1196                // Check for field-level proxy
1197                if let Some(proxy_def) = field_item.field.and_then(|f| f.proxy()) {
1198                    serialize_via_proxy(serializer, field_value, proxy_def)?;
1199                } else {
1200                    shared_serialize(serializer, field_value)?;
1201                }
1202            }
1203            serializer.end_struct().map_err(SerializeError::Backend)?;
1204            Ok(())
1205        }
1206    }
1207}
1208
1209/// Dereference a pointer/reference (Box, Arc, etc.) to get the underlying value
1210fn deref_if_pointer<'mem, 'facet>(peek: Peek<'mem, 'facet>) -> Peek<'mem, 'facet> {
1211    if let Ok(ptr) = peek.into_pointer()
1212        && let Some(target) = ptr.borrow_inner()
1213    {
1214        return deref_if_pointer(target);
1215    }
1216    peek
1217}
1218
1219/// Serialize a value through its proxy type.
1220///
1221/// # Safety note
1222/// This function requires unsafe code to:
1223/// - Allocate memory for the proxy type
1224/// - Call the conversion function from target to proxy
1225/// - Drop the proxy value after serialization
1226#[allow(unsafe_code)]
1227fn serialize_via_proxy<'mem, 'facet, S>(
1228    serializer: &mut S,
1229    value: Peek<'mem, 'facet>,
1230    proxy_def: &'static facet_core::ProxyDef,
1231) -> Result<(), SerializeError<S::Error>>
1232where
1233    S: FormatSerializer,
1234{
1235    use facet_core::PtrUninit;
1236
1237    let proxy_shape = proxy_def.shape;
1238    let proxy_layout = proxy_shape.layout.sized_layout().map_err(|_| {
1239        SerializeError::Unsupported(Cow::Borrowed("proxy type must be sized for serialization"))
1240    })?;
1241
1242    // Allocate memory for the proxy value
1243    let proxy_mem = unsafe { alloc::alloc::alloc(proxy_layout) };
1244    if proxy_mem.is_null() {
1245        return Err(SerializeError::Internal(Cow::Borrowed(
1246            "failed to allocate proxy memory",
1247        )));
1248    }
1249
1250    // Convert target → proxy
1251    let proxy_uninit = PtrUninit::new(proxy_mem);
1252    let convert_result = unsafe { (proxy_def.convert_out)(value.data(), proxy_uninit) };
1253
1254    let proxy_ptr = match convert_result {
1255        Ok(ptr) => ptr,
1256        Err(msg) => {
1257            unsafe { alloc::alloc::dealloc(proxy_mem, proxy_layout) };
1258            return Err(SerializeError::Unsupported(Cow::Owned(msg)));
1259        }
1260    };
1261
1262    // Create a Peek to the proxy value and serialize it
1263    let proxy_peek = unsafe { Peek::unchecked_new(proxy_ptr.as_const(), proxy_shape) };
1264    let result = shared_serialize(serializer, proxy_peek);
1265
1266    // Clean up: drop the proxy value and deallocate
1267    unsafe {
1268        let _ = proxy_shape.call_drop_in_place(proxy_ptr);
1269        alloc::alloc::dealloc(proxy_mem, proxy_layout);
1270    }
1271
1272    result
1273}