diff --git a/facet-reflect/src/peek/enum_.rs b/facet-reflect/src/peek/enum_.rs index 90d1c3f59..511bfbed6 100644 --- a/facet-reflect/src/peek/enum_.rs +++ b/facet-reflect/src/peek/enum_.rs @@ -1,8 +1,8 @@ -use facet_core::{EnumRepr, EnumType, Field, Shape, UserType, Variant}; +use facet_core::{EnumRepr, EnumType, Shape, UserType, Variant}; use crate::{Peek, trace}; -use super::HasFields; +use super::{FieldIter, HasFields}; /// Lets you read from an enum (implements read-only enum operations) #[derive(Clone, Copy)] @@ -243,29 +243,8 @@ impl<'mem, 'facet, 'shape> PeekEnum<'mem, 'facet, 'shape> { } impl<'mem, 'facet, 'shape> HasFields<'mem, 'facet, 'shape> for PeekEnum<'mem, 'facet, 'shape> { - fn fields( - &self, - ) -> impl DoubleEndedIterator, Peek<'mem, 'facet, 'shape>)> { - // Get the active variant and its fields - let variant = match self.active_variant() { - Ok(v) => v, - Err(e) => panic!("Cannot get active variant: {:?}", e), - }; - let fields = &variant.data.fields; - - // Create an iterator that yields the field definition and field value - (0..fields.len()).filter_map(move |i| { - // Get the field definition - let field = fields[i]; - // Get the field value - let field_value = match self.field(i) { - Ok(Some(v)) => v, - Ok(None) => return None, - Err(e) => panic!("Cannot get field: {:?}", e), - }; - // Return the field definition and value - Some((field, field_value)) - }) + fn fields(&self) -> FieldIter<'mem, 'facet, 'shape> { + FieldIter::new_enum(*self) } } diff --git a/facet-reflect/src/peek/fields.rs b/facet-reflect/src/peek/fields.rs new file mode 100644 index 000000000..bd68e7941 --- /dev/null +++ b/facet-reflect/src/peek/fields.rs @@ -0,0 +1,207 @@ +use core::ops::Range; + +use facet_core::{Field, FieldFlags}; + +use crate::Peek; +use alloc::{vec, vec::Vec}; + +use super::{PeekEnum, PeekStruct, PeekTuple}; + +/// Trait for types that have field methods +/// +/// This trait allows code to be written generically over both structs and enums +/// that provide field access and iteration capabilities. +pub trait HasFields<'mem, 'facet, 'shape> { + /// Iterates over all fields in this type, providing both field metadata and value + fn fields(&self) -> FieldIter<'mem, 'facet, 'shape>; + + /// Iterates over fields in this type that should be included when it is serialized + fn fields_for_serialize(&self) -> FieldsForSerializeIter<'mem, 'facet, 'shape> { + FieldsForSerializeIter { + stack: vec![self.fields()], + } + } +} + +/// An iterator over all the fields of a struct or enum. See [`HasFields::fields`] +pub struct FieldIter<'mem, 'facet, 'shape> { + state: FieldIterState<'mem, 'facet, 'shape>, + range: Range, +} + +enum FieldIterState<'mem, 'facet, 'shape> { + Struct(PeekStruct<'mem, 'facet, 'shape>), + Tuple(PeekTuple<'mem, 'facet, 'shape>), + Enum { + peek_enum: PeekEnum<'mem, 'facet, 'shape>, + fields: &'shape [Field<'shape>], + }, + FlattenedEnum { + field: Field<'shape>, + value: Peek<'mem, 'facet, 'shape>, + }, +} + +impl<'mem, 'facet, 'shape> FieldIter<'mem, 'facet, 'shape> { + pub(crate) fn new_struct(struct_: PeekStruct<'mem, 'facet, 'shape>) -> Self { + Self { + range: 0..struct_.ty.fields.len(), + state: FieldIterState::Struct(struct_), + } + } + + pub(crate) fn new_enum(enum_: PeekEnum<'mem, 'facet, 'shape>) -> Self { + // Get the fields of the active variant + let variant = match enum_.active_variant() { + Ok(v) => v, + Err(e) => panic!("Cannot get active variant: {:?}", e), + }; + let fields = &variant.data.fields; + + Self { + range: 0..fields.len(), + state: FieldIterState::Enum { + peek_enum: enum_, + fields, + }, + } + } + + pub(crate) fn new_tuple(tuple: PeekTuple<'mem, 'facet, 'shape>) -> Self { + Self { + range: 0..tuple.len(), + state: FieldIterState::Tuple(tuple), + } + } + + fn get_field_by_index( + &self, + index: usize, + ) -> Option<(Field<'shape>, Peek<'mem, 'facet, 'shape>)> { + match self.state { + FieldIterState::Struct(peek_struct) => { + let field = peek_struct.ty.fields.get(index).copied()?; + let value = peek_struct.field(index).ok()?; + Some((field, value)) + } + FieldIterState::Tuple(peek_tuple) => { + let field = peek_tuple.ty.fields.get(index).copied()?; + let value = peek_tuple.field(index)?; + Some((field, value)) + } + FieldIterState::Enum { peek_enum, fields } => { + // Get the field definition + let field = fields[index]; + // Get the field value + let field_value = match peek_enum.field(index) { + Ok(Some(v)) => v, + Ok(None) => return None, + Err(e) => panic!("Cannot get field: {:?}", e), + }; + // Return the field definition and value + Some((field, field_value)) + } + FieldIterState::FlattenedEnum { field, value } => { + if index == 0 { + Some((field, value)) + } else { + None + } + } + } + } +} + +impl<'mem, 'facet, 'shape> Iterator for FieldIter<'mem, 'facet, 'shape> { + type Item = (Field<'shape>, Peek<'mem, 'facet, 'shape>); + + fn next(&mut self) -> Option { + loop { + let index = self.range.next()?; + + let Some(field) = self.get_field_by_index(index) else { + continue; + }; + + return Some(field); + } + } + + fn size_hint(&self) -> (usize, Option) { + self.range.size_hint() + } +} + +impl DoubleEndedIterator for FieldIter<'_, '_, '_> { + fn next_back(&mut self) -> Option { + loop { + let index = self.range.next_back()?; + + let Some(field) = self.get_field_by_index(index) else { + continue; + }; + + return Some(field); + } + } +} + +impl ExactSizeIterator for FieldIter<'_, '_, '_> {} + +/// An iterator over the fields of a struct or enum that should be serialized. See [`HasFields::fields_for_serialize`] +pub struct FieldsForSerializeIter<'mem, 'facet, 'shape> { + stack: Vec>, +} + +impl<'mem, 'facet, 'shape> Iterator for FieldsForSerializeIter<'mem, 'facet, 'shape> { + type Item = (Field<'shape>, Peek<'mem, 'facet, 'shape>); + + fn next(&mut self) -> Option { + loop { + let mut fields = self.stack.pop()?; + let Some((mut field, peek)) = fields.next() else { + continue; + }; + self.stack.push(fields); + + let should_skip = unsafe { field.should_skip_serializing(peek.data()) }; + if should_skip { + continue; + } + + if field.flags.contains(FieldFlags::FLATTEN) && !field.flattened { + if let Ok(struct_peek) = peek.into_struct() { + self.stack.push(FieldIter::new_struct(struct_peek)) + } else if let Ok(enum_peek) = peek.into_enum() { + // normally we'd serialize to something like: + // + // { + // "field_on_struct": { + // "VariantName": { "field_on_variant": "foo" } + // } + // } + // + // But since `field_on_struct` is flattened, instead we do: + // + // { + // "VariantName": { "field_on_variant": "foo" } + // } + field.name = enum_peek + .active_variant() + .expect("Failed to get active variant") + .name; + field.flattened = true; + self.stack.push(FieldIter { + range: 0..1, + state: FieldIterState::FlattenedEnum { field, value: peek }, + }); + } else { + // TODO: fail more gracefully + panic!("cannot flatten a {}", field.shape()) + } + } else { + return Some((field, peek)); + } + } + } +} diff --git a/facet-reflect/src/peek/mod.rs b/facet-reflect/src/peek/mod.rs index c6927e952..44e9c90a3 100644 --- a/facet-reflect/src/peek/mod.rs +++ b/facet-reflect/src/peek/mod.rs @@ -9,6 +9,9 @@ pub use struct_::*; mod enum_; pub use enum_::*; +mod fields; +pub use fields::*; + mod list; pub use list::*; diff --git a/facet-reflect/src/peek/struct_.rs b/facet-reflect/src/peek/struct_.rs index bddadb81b..eba1239d2 100644 --- a/facet-reflect/src/peek/struct_.rs +++ b/facet-reflect/src/peek/struct_.rs @@ -1,7 +1,8 @@ -use facet_core::{Field, FieldError, FieldFlags, StructType}; +use facet_core::{FieldError, StructType}; use crate::Peek; -use alloc::{vec, vec::Vec}; + +use super::{FieldIter, HasFields}; /// Lets you read from a struct (implements read-only struct operations) #[derive(Clone, Copy)] @@ -63,70 +64,7 @@ impl<'mem, 'facet, 'shape> PeekStruct<'mem, 'facet, 'shape> { impl<'mem, 'facet, 'shape> HasFields<'mem, 'facet, 'shape> for PeekStruct<'mem, 'facet, 'shape> { /// Iterates over all fields in this struct, providing both name and value #[inline] - fn fields( - &self, - ) -> impl DoubleEndedIterator, Peek<'mem, 'facet, 'shape>)> { - (0..self.field_count()).filter_map(|i| { - let field = self.ty.fields.get(i).copied()?; - let value = self.field(i).ok()?; - Some((field, value)) - }) - } -} - -/// Trait for types that have field methods -/// -/// This trait allows code to be written generically over both structs and enums -/// that provide field access and iteration capabilities. -pub trait HasFields<'mem, 'facet, 'shape> { - /// Iterates over all fields in this type, providing both field metadata and value - fn fields( - &self, - ) -> impl DoubleEndedIterator, Peek<'mem, 'facet, 'shape>)>; - - /// Iterates over fields in this type that should be included when it is serialized - fn fields_for_serialize( - &self, - ) -> impl DoubleEndedIterator, Peek<'mem, 'facet, 'shape>)> { - // This is a default implementation that filters out fields with `skip_serializing` - // attribute and handles field flattening. - self.fields() - .filter(|(field, peek)| !unsafe { field.should_skip_serializing(peek.data()) }) - .flat_map(move |(mut field, peek)| { - if field.flags.contains(FieldFlags::FLATTEN) { - let mut flattened = Vec::new(); - if let Ok(struct_peek) = peek.into_struct() { - struct_peek - .fields_for_serialize() - .for_each(|item| flattened.push(item)); - } else if let Ok(enum_peek) = peek.into_enum() { - // normally we'd serialize to something like: - // - // { - // "field_on_struct": { - // "VariantName": { "field_on_variant": "foo" } - // } - // } - // - // But since `field_on_struct` is flattened, instead we do: - // - // { - // "VariantName": { "field_on_variant": "foo" } - // } - field.name = enum_peek - .active_variant() - .expect("Failed to get active variant") - .name; - field.flattened = true; - flattened.push((field, peek)); - } else { - // TODO: fail more gracefully - panic!("cannot flatten a {}", field.shape()) - } - flattened - } else { - vec![(field, peek)] - } - }) + fn fields(&self) -> FieldIter<'mem, 'facet, 'shape> { + FieldIter::new_struct(*self) } } diff --git a/facet-reflect/src/peek/tuple.rs b/facet-reflect/src/peek/tuple.rs index e934e9747..a454cc93f 100644 --- a/facet-reflect/src/peek/tuple.rs +++ b/facet-reflect/src/peek/tuple.rs @@ -1,7 +1,7 @@ use core::fmt::Debug; use facet_core::TupleType; -use super::Peek; +use super::{FieldIter, Peek}; /// Field index and associated peek value pub type TupleField<'mem, 'facet, 'shape> = (usize, Peek<'mem, 'facet, 'shape>); @@ -50,8 +50,8 @@ impl<'mem, 'facet, 'shape> PeekTuple<'mem, 'facet, 'shape> { } /// Iterate over all fields - pub fn fields(&self) -> impl DoubleEndedIterator> + '_ { - (0..self.len()).filter_map(|i| self.field(i).map(|p| (i, p))) + pub fn fields(&self) -> FieldIter<'mem, 'facet, 'shape> { + FieldIter::new_tuple(*self) } /// Type information diff --git a/facet-serialize/src/lib.rs b/facet-serialize/src/lib.rs index 3e6c6b456..6acc1a5b8 100644 --- a/facet-serialize/src/lib.rs +++ b/facet-serialize/src/lib.rs @@ -14,7 +14,9 @@ use facet_core::{ Def, Facet, Field, PointerType, ScalarAffinity, SequenceType, ShapeAttribute, StructKind, Type, UserType, }; -use facet_reflect::{HasFields, Peek, PeekListLike, PeekMap, PeekStruct, PeekTuple, ScalarType}; +use facet_reflect::{ + FieldIter, FieldsForSerializeIter, HasFields, Peek, PeekListLikeIter, PeekMapIter, ScalarType, +}; use log::{debug, trace}; mod debug_serializer; @@ -223,22 +225,37 @@ pub trait Serializer<'shape> { // --- Iterative Serialization Logic --- /// Task items for the serialization stack. -#[derive(Debug)] enum SerializeTask<'mem, 'facet, 'shape> { Value(Peek<'mem, 'facet, 'shape>, Option>), + Object { + entries: FieldsForSerializeIter<'mem, 'facet, 'shape>, + first: bool, + len: usize, + }, + Array { + items: PeekListLikeIter<'mem, 'facet, 'shape>, + first: bool, + }, + Map { + entries: PeekMapIter<'mem, 'facet, 'shape>, + first: bool, + len: usize, + }, + TupleStruct { + items: FieldsForSerializeIter<'mem, 'facet, 'shape>, + first: bool, + len: usize, + }, + Tuple { + items: FieldIter<'mem, 'facet, 'shape>, + first: bool, + }, // End markers EndObject, EndArray, - EndMap, EndMapKey, EndMapValue, EndField, - // Tasks to push sub-elements onto the stack - ObjectFields(PeekStruct<'mem, 'facet, 'shape>), - ArrayItems(PeekListLike<'mem, 'facet, 'shape>), - TupleStructFields(PeekStruct<'mem, 'facet, 'shape>), - TupleFields(PeekTuple<'mem, 'facet, 'shape>), - MapEntries(PeekMap<'mem, 'facet, 'shape>), // Field-related tasks SerializeFieldName(&'shape str), SerializeMapKey(Peek<'mem, 'facet, 'shape>), @@ -385,10 +402,10 @@ where serializer.serialize_bytes(cpeek.get::>().unwrap())? } else { let peek_list = cpeek.into_list_like().unwrap(); - let len = peek_list.len(); - serializer.start_array(Some(len))?; - stack.push(SerializeTask::EndArray); - stack.push(SerializeTask::ArrayItems(peek_list)); + stack.push(SerializeTask::Array { + items: peek_list.iter(), + first: true, + }); } } (Def::Array(ad), _) => { @@ -402,10 +419,10 @@ where serializer.serialize_bytes(&bytes)?; } else { let peek_list = cpeek.into_list_like().unwrap(); - let len = peek_list.len(); - serializer.start_array(Some(len))?; - stack.push(SerializeTask::EndArray); - stack.push(SerializeTask::ArrayItems(peek_list)); + stack.push(SerializeTask::Array { + items: peek_list.iter(), + first: true, + }); } } (Def::Slice(sd), _) => { @@ -413,18 +430,20 @@ where serializer.serialize_bytes(cpeek.get::<&[u8]>().unwrap())? } else { let peek_list = cpeek.into_list_like().unwrap(); - let len = peek_list.len(); - serializer.start_array(Some(len))?; - stack.push(SerializeTask::EndArray); - stack.push(SerializeTask::ArrayItems(peek_list)); + stack.push(SerializeTask::Array { + items: peek_list.iter(), + first: true, + }); } } (Def::Map(_), _) => { let peek_map = cpeek.into_map().unwrap(); let len = peek_map.len(); - serializer.start_map(Some(len))?; - stack.push(SerializeTask::EndMap); - stack.push(SerializeTask::MapEntries(peek_map)); + stack.push(SerializeTask::Map { + entries: peek_map.iter(), + first: true, + len, + }); } (Def::Option(_), _) => { let opt = cpeek.into_option().unwrap(); @@ -458,9 +477,11 @@ where let fields = peek_struct.fields_for_serialize().count(); debug!(" Serializing {} fields as array", fields); - serializer.start_array(Some(fields))?; - stack.push(SerializeTask::EndArray); - stack.push(SerializeTask::TupleStructFields(peek_struct)); + stack.push(SerializeTask::TupleStruct { + items: peek_struct.fields_for_serialize(), + first: true, + len: fields, + }); trace!( " Pushed TupleStructFields to stack, will handle {} fields", fields @@ -472,9 +493,11 @@ where let fields = peek_struct.fields_for_serialize().count(); debug!(" Serializing {} fields as object", fields); - serializer.start_object(Some(fields))?; - stack.push(SerializeTask::EndObject); - stack.push(SerializeTask::ObjectFields(peek_struct)); + stack.push(SerializeTask::Object { + entries: peek_struct.fields_for_serialize(), + first: true, + len: fields, + }); trace!( " Pushed ObjectFields to stack, will handle {} fields", fields @@ -493,9 +516,10 @@ where let count = peek_tuple.len(); debug!(" Tuple fields count: {}", count); - serializer.start_array(Some(count))?; - stack.push(SerializeTask::EndArray); - stack.push(SerializeTask::TupleFields(peek_tuple)); + stack.push(SerializeTask::Tuple { + items: peek_tuple.fields(), + first: true, + }); trace!( " Pushed TupleFields to stack for tuple, will handle {} fields", count @@ -508,10 +532,10 @@ where ); if let Ok(peek_list_like) = cpeek.into_list_like() { - let count = peek_list_like.len(); - serializer.start_array(Some(count))?; - stack.push(SerializeTask::EndArray); - stack.push(SerializeTask::ArrayItems(peek_list_like)); + stack.push(SerializeTask::Array { + items: peek_list_like.iter(), + first: true, + }); trace!(" Pushed ArrayItems to stack for tuple serialization",); } else { // Final fallback - create an empty array @@ -519,7 +543,7 @@ where " Could not convert tuple to list-like either, using empty array" ); serializer.start_array(Some(0))?; - stack.push(SerializeTask::EndArray); + serializer.end_array()?; trace!(" Warning: Tuple serialization fallback to empty array"); } } @@ -571,7 +595,9 @@ where stack.push(SerializeTask::EndArray); // Push fields in reverse order for tuple variant - for (field, field_peek) in peek_enum.fields_for_serialize().rev() { + let fields_for_serialize = + peek_enum.fields_for_serialize().collect::>(); + for (field, field_peek) in fields_for_serialize.into_iter().rev() { stack.push(SerializeTask::Value(field_peek, Some(field))); } } else { @@ -581,7 +607,9 @@ where stack.push(SerializeTask::EndObject); // Push fields in reverse order for struct variant - for (field, field_peek) in peek_enum.fields_for_serialize().rev() { + let fields_for_serialize = + peek_enum.fields_for_serialize().collect::>(); + for (field, field_peek) in fields_for_serialize.into_iter().rev() { stack.push(SerializeTask::EndField); stack.push(SerializeTask::Value(field_peek, Some(field))); stack.push(SerializeTask::SerializeFieldName(field.name)); @@ -620,47 +648,103 @@ where } } - // --- Pushing sub-elements onto the stack --- - SerializeTask::ObjectFields(peek_struct) => { - // Push fields in reverse order for stack processing - for (field, field_peek) in peek_struct.fields_for_serialize().rev() { - stack.push(SerializeTask::EndField); - stack.push(SerializeTask::Value(field_peek, Some(field))); - stack.push(SerializeTask::SerializeFieldName(field.name)); + SerializeTask::Object { + mut entries, + first, + len, + } => { + if first { + serializer.start_object(Some(len))?; } + + let Some((field, value)) = entries.next() else { + serializer.end_object()?; + continue; + }; + + stack.push(SerializeTask::Object { + entries, + first: false, + len, + }); + stack.push(SerializeTask::EndField); + stack.push(SerializeTask::Value(value, Some(field))); + stack.push(SerializeTask::SerializeFieldName(field.name)); } - SerializeTask::TupleStructFields(peek_struct) => { - // Push fields in reverse order - for (field, field_peek) in peek_struct.fields_for_serialize().rev() { - stack.push(SerializeTask::Value(field_peek, Some(field))); + SerializeTask::Array { mut items, first } => { + if first { + serializer.start_array(Some(items.len()))?; } + + let Some(value) = items.next() else { + serializer.end_array()?; + continue; + }; + + stack.push(SerializeTask::Array { + items, + first: false, + }); + stack.push(SerializeTask::Value(value, None)); } - SerializeTask::TupleFields(peek_tuple) => { - // Push fields in reverse order - for (_, field_peek) in peek_tuple.fields().rev() { - // Get the innermost peek value - this is essential for proper serialization - // to unwrap transparent wrappers and get to the actual value - let innermost_peek = field_peek.innermost_peek(); - - // Push the innermost peek to the stack - stack.push(SerializeTask::Value(innermost_peek, None)); + SerializeTask::Map { + mut entries, + first, + len, + } => { + if first { + serializer.start_map(Some(len))?; } - trace!(" Pushed {} tuple fields to stack", peek_tuple.len()); + + let Some((key, value)) = entries.next() else { + serializer.end_map()?; + continue; + }; + + stack.push(SerializeTask::Map { + entries, + first: false, + len, + }); + stack.push(SerializeTask::SerializeMapValue(value)); + stack.push(SerializeTask::SerializeMapKey(key)); } - SerializeTask::ArrayItems(peek_list) => { - // Push items in reverse order - let items: Vec<_> = peek_list.iter().collect(); - for item_peek in items.into_iter().rev() { - stack.push(SerializeTask::Value(item_peek, None)); + SerializeTask::TupleStruct { + mut items, + first, + len, + } => { + if first { + serializer.start_array(Some(len))?; } + + let Some((field, value)) = items.next() else { + serializer.end_array()?; + continue; + }; + + stack.push(SerializeTask::TupleStruct { + items, + first: false, + len, + }); + stack.push(SerializeTask::Value(value, Some(field))); } - SerializeTask::MapEntries(peek_map) => { - // Push entries in reverse order (key, value pairs) - let entries = peek_map.iter().collect::>(); - for (key_peek, value_peek) in entries.into_iter().rev() { - stack.push(SerializeTask::SerializeMapValue(value_peek)); - stack.push(SerializeTask::SerializeMapKey(key_peek)); + SerializeTask::Tuple { mut items, first } => { + if first { + serializer.start_array(Some(items.len()))?; } + + let Some((field, value)) = items.next() else { + serializer.end_array()?; + continue; + }; + + stack.push(SerializeTask::Tuple { + items, + first: false, + }); + stack.push(SerializeTask::Value(value, Some(field))); } // --- Field name and map key/value handling --- @@ -685,9 +769,6 @@ where SerializeTask::EndArray => { serializer.end_array()?; } - SerializeTask::EndMap => { - serializer.end_map()?; - } SerializeTask::EndMapKey => { serializer.end_map_key()?; }