facet_reflect/peek/
fields.rs

1use core::ops::Range;
2
3use alloc::borrow::Cow;
4use facet_core::Field;
5
6use crate::Peek;
7use alloc::{string::String, vec, vec::Vec};
8
9use super::{PeekEnum, PeekStruct, PeekTuple};
10
11/// A field item with runtime state for serialization.
12///
13/// This wraps a static `Field` with additional runtime state that can be modified
14/// during iteration (e.g., for flattened enums where the field name becomes the variant name,
15/// or for flattened maps where entries become synthetic fields).
16#[derive(Clone, Debug)]
17pub struct FieldItem {
18    /// The underlying static field definition (None for flattened map entries)
19    pub field: Option<Field>,
20    /// Runtime-determined name (may differ from field.name for flattened enums/maps)
21    pub name: Cow<'static, str>,
22    /// Whether this field was flattened from an enum (variant name used as key)
23    pub flattened: bool,
24}
25
26impl FieldItem {
27    /// Create a new FieldItem from a Field, using the field's name
28    #[inline]
29    pub fn new(field: Field) -> Self {
30        Self {
31            name: Cow::Borrowed(field.name),
32            field: Some(field),
33            flattened: false,
34        }
35    }
36
37    /// Create a flattened enum field item with a custom name (the variant name)
38    #[inline]
39    pub fn flattened_enum(field: Field, variant_name: &'static str) -> Self {
40        Self {
41            name: Cow::Borrowed(variant_name),
42            field: Some(field),
43            flattened: true,
44        }
45    }
46
47    /// Create a flattened map entry field item with a dynamic key
48    #[inline]
49    pub fn flattened_map_entry(key: String) -> Self {
50        Self {
51            name: Cow::Owned(key),
52            field: None,
53            flattened: true,
54        }
55    }
56}
57
58/// Trait for types that have field methods
59///
60/// This trait allows code to be written generically over both structs and enums
61/// that provide field access and iteration capabilities.
62pub trait HasFields<'mem, 'facet> {
63    /// Iterates over all fields in this type, providing both field metadata and value
64    fn fields(&self) -> FieldIter<'mem, 'facet>;
65
66    /// Iterates over fields in this type that should be included when it is serialized
67    fn fields_for_serialize(&self) -> FieldsForSerializeIter<'mem, 'facet> {
68        FieldsForSerializeIter {
69            stack: vec![FieldsForSerializeIterState::Fields(self.fields())],
70        }
71    }
72}
73
74/// An iterator over all the fields of a struct or enum. See [`HasFields::fields`]
75pub struct FieldIter<'mem, 'facet> {
76    state: FieldIterState<'mem, 'facet>,
77    range: Range<usize>,
78}
79
80enum FieldIterState<'mem, 'facet> {
81    Struct(PeekStruct<'mem, 'facet>),
82    Tuple(PeekTuple<'mem, 'facet>),
83    Enum {
84        peek_enum: PeekEnum<'mem, 'facet>,
85        fields: &'static [Field],
86    },
87}
88
89impl<'mem, 'facet> FieldIter<'mem, 'facet> {
90    #[inline]
91    pub(crate) fn new_struct(struct_: PeekStruct<'mem, 'facet>) -> Self {
92        Self {
93            range: 0..struct_.ty.fields.len(),
94            state: FieldIterState::Struct(struct_),
95        }
96    }
97
98    #[inline]
99    pub(crate) fn new_enum(enum_: PeekEnum<'mem, 'facet>) -> Self {
100        // Get the fields of the active variant
101        let variant = match enum_.active_variant() {
102            Ok(v) => v,
103            Err(e) => panic!("Cannot get active variant: {e:?}"),
104        };
105        let fields = &variant.data.fields;
106
107        Self {
108            range: 0..fields.len(),
109            state: FieldIterState::Enum {
110                peek_enum: enum_,
111                fields,
112            },
113        }
114    }
115
116    #[inline]
117    pub(crate) fn new_tuple(tuple: PeekTuple<'mem, 'facet>) -> Self {
118        Self {
119            range: 0..tuple.len(),
120            state: FieldIterState::Tuple(tuple),
121        }
122    }
123
124    fn get_field_by_index(&self, index: usize) -> Option<(Field, Peek<'mem, 'facet>)> {
125        match self.state {
126            FieldIterState::Struct(peek_struct) => {
127                let field = peek_struct.ty.fields.get(index).copied()?;
128                let value = peek_struct.field(index).ok()?;
129                Some((field, value))
130            }
131            FieldIterState::Tuple(peek_tuple) => {
132                let field = peek_tuple.ty.fields.get(index).copied()?;
133                let value = peek_tuple.field(index)?;
134                Some((field, value))
135            }
136            FieldIterState::Enum { peek_enum, fields } => {
137                // Get the field definition
138                let field = fields[index];
139                // Get the field value
140                let field_value = match peek_enum.field(index) {
141                    Ok(Some(v)) => v,
142                    Ok(None) => return None,
143                    Err(e) => panic!("Cannot get field: {e:?}"),
144                };
145                // Return the field definition and value
146                Some((field, field_value))
147            }
148        }
149    }
150}
151
152impl<'mem, 'facet> Iterator for FieldIter<'mem, 'facet> {
153    type Item = (Field, Peek<'mem, 'facet>);
154
155    #[inline]
156    fn next(&mut self) -> Option<Self::Item> {
157        loop {
158            let index = self.range.next()?;
159
160            let Some(field) = self.get_field_by_index(index) else {
161                continue;
162            };
163
164            return Some(field);
165        }
166    }
167
168    #[inline]
169    fn size_hint(&self) -> (usize, Option<usize>) {
170        self.range.size_hint()
171    }
172}
173
174impl DoubleEndedIterator for FieldIter<'_, '_> {
175    #[inline]
176    fn next_back(&mut self) -> Option<Self::Item> {
177        loop {
178            let index = self.range.next_back()?;
179
180            let Some(field) = self.get_field_by_index(index) else {
181                continue;
182            };
183
184            return Some(field);
185        }
186    }
187}
188
189impl ExactSizeIterator for FieldIter<'_, '_> {}
190
191/// An iterator over the fields of a struct or enum that should be serialized. See [`HasFields::fields_for_serialize`]
192pub struct FieldsForSerializeIter<'mem, 'facet> {
193    stack: Vec<FieldsForSerializeIterState<'mem, 'facet>>,
194}
195
196enum FieldsForSerializeIterState<'mem, 'facet> {
197    /// Normal field iteration
198    Fields(FieldIter<'mem, 'facet>),
199    /// A single flattened enum item to yield
200    FlattenedEnum {
201        field_item: Option<FieldItem>,
202        value: Peek<'mem, 'facet>,
203    },
204    /// Iterating over a flattened map's entries
205    FlattenedMap {
206        map_iter: super::PeekMapIter<'mem, 'facet>,
207    },
208}
209
210impl<'mem, 'facet> Iterator for FieldsForSerializeIter<'mem, 'facet> {
211    type Item = (FieldItem, Peek<'mem, 'facet>);
212
213    fn next(&mut self) -> Option<Self::Item> {
214        loop {
215            let state = self.stack.pop()?;
216
217            match state {
218                FieldsForSerializeIterState::FlattenedEnum { field_item, value } => {
219                    // Yield the flattened enum item (only once)
220                    if let Some(item) = field_item {
221                        return Some((item, value));
222                    }
223                    // Already yielded, continue to next state
224                    continue;
225                }
226                FieldsForSerializeIterState::FlattenedMap { mut map_iter } => {
227                    // Iterate over map entries, yielding each as a synthetic field
228                    if let Some((key_peek, value_peek)) = map_iter.next() {
229                        // Push iterator back for more entries
230                        self.stack
231                            .push(FieldsForSerializeIterState::FlattenedMap { map_iter });
232                        // Get the key as a string using Display trait
233                        // This works for String, SmolStr, SmartString, CompactString, etc.
234                        if key_peek.shape().vtable.has_display() {
235                            use alloc::string::ToString;
236                            let key_str = key_peek.to_string();
237                            let field_item = FieldItem::flattened_map_entry(key_str);
238                            return Some((field_item, value_peek));
239                        }
240                        // Skip entries with non-string-like keys
241                        continue;
242                    }
243                    // Map exhausted, continue to next state
244                    continue;
245                }
246                FieldsForSerializeIterState::Fields(mut fields) => {
247                    let Some((field, peek)) = fields.next() else {
248                        continue;
249                    };
250                    self.stack.push(FieldsForSerializeIterState::Fields(fields));
251
252                    let data = peek.data();
253                    let should_skip = unsafe { field.should_skip_serializing(data) };
254
255                    if should_skip {
256                        continue;
257                    }
258
259                    if field.is_flattened() {
260                        if let Ok(struct_peek) = peek.into_struct() {
261                            self.stack.push(FieldsForSerializeIterState::Fields(
262                                FieldIter::new_struct(struct_peek),
263                            ))
264                        } else if let Ok(enum_peek) = peek.into_enum() {
265                            // normally we'd serialize to something like:
266                            //
267                            //   {
268                            //     "field_on_struct": {
269                            //       "VariantName": { "field_on_variant": "foo" }
270                            //     }
271                            //   }
272                            //
273                            // But since `field_on_struct` is flattened, instead we do:
274                            //
275                            //   {
276                            //     "VariantName": { "field_on_variant": "foo" }
277                            //   }
278                            //
279                            // To achieve this, we emit the variant name as the field key
280                            // and the variant's inner value (not the whole enum) as the value.
281                            let variant = enum_peek
282                                .active_variant()
283                                .expect("Failed to get active variant");
284                            let field_item = FieldItem::flattened_enum(field, variant.name);
285
286                            // Get the inner value based on variant kind
287                            use facet_core::StructKind;
288                            let inner_value = match variant.data.kind {
289                                StructKind::Unit => {
290                                    // Unit variants have no inner value - skip
291                                    continue;
292                                }
293                                StructKind::TupleStruct | StructKind::Tuple
294                                    if variant.data.fields.len() == 1 =>
295                                {
296                                    // Newtype variant - yield the inner value directly
297                                    enum_peek
298                                        .field(0)
299                                        .expect("Failed to get variant field")
300                                        .expect("Newtype variant should have field 0")
301                                }
302                                StructKind::TupleStruct
303                                | StructKind::Tuple
304                                | StructKind::Struct => {
305                                    // Multi-field tuple or struct variant - push fields iterator
306                                    self.stack.push(FieldsForSerializeIterState::FlattenedEnum {
307                                        field_item: Some(field_item),
308                                        value: peek,
309                                    });
310                                    // The serializer will handle enum variant serialization
311                                    // which will emit the variant's fields/array properly
312                                    continue;
313                                }
314                            };
315
316                            self.stack.push(FieldsForSerializeIterState::FlattenedEnum {
317                                field_item: Some(field_item),
318                                value: inner_value,
319                            });
320                        } else if let Ok(map_peek) = peek.into_map() {
321                            // Flattened map - emit entries as synthetic fields
322                            self.stack.push(FieldsForSerializeIterState::FlattenedMap {
323                                map_iter: map_peek.iter(),
324                            });
325                        } else if let Ok(option_peek) = peek.into_option() {
326                            // Option<T> where T is a struct, enum, or map
327                            // If Some, flatten the inner value; if None, skip entirely
328                            if let Some(inner_peek) = option_peek.value() {
329                                if let Ok(struct_peek) = inner_peek.into_struct() {
330                                    self.stack.push(FieldsForSerializeIterState::Fields(
331                                        FieldIter::new_struct(struct_peek),
332                                    ))
333                                } else if let Ok(enum_peek) = inner_peek.into_enum() {
334                                    let variant = enum_peek
335                                        .active_variant()
336                                        .expect("Failed to get active variant");
337                                    let field_item = FieldItem::flattened_enum(field, variant.name);
338
339                                    // Get the inner value based on variant kind
340                                    use facet_core::StructKind;
341                                    let inner_value = match variant.data.kind {
342                                        StructKind::Unit => {
343                                            // Unit variants have no inner value - skip
344                                            continue;
345                                        }
346                                        StructKind::TupleStruct | StructKind::Tuple
347                                            if variant.data.fields.len() == 1 =>
348                                        {
349                                            // Newtype variant - yield the inner value directly
350                                            enum_peek
351                                                .field(0)
352                                                .expect("Failed to get variant field")
353                                                .expect("Newtype variant should have field 0")
354                                        }
355                                        StructKind::TupleStruct
356                                        | StructKind::Tuple
357                                        | StructKind::Struct => {
358                                            // Multi-field tuple or struct variant
359                                            self.stack.push(
360                                                FieldsForSerializeIterState::FlattenedEnum {
361                                                    field_item: Some(field_item),
362                                                    value: inner_peek,
363                                                },
364                                            );
365                                            continue;
366                                        }
367                                    };
368
369                                    self.stack.push(FieldsForSerializeIterState::FlattenedEnum {
370                                        field_item: Some(field_item),
371                                        value: inner_value,
372                                    });
373                                } else if let Ok(map_peek) = inner_peek.into_map() {
374                                    self.stack.push(FieldsForSerializeIterState::FlattenedMap {
375                                        map_iter: map_peek.iter(),
376                                    });
377                                } else {
378                                    panic!(
379                                        "cannot flatten Option<{}> - inner type must be struct, enum, or map",
380                                        inner_peek.shape()
381                                    )
382                                }
383                            }
384                            // If None, we just skip - don't emit any fields
385                        } else {
386                            // TODO: fail more gracefully
387                            panic!("cannot flatten a {}", field.shape())
388                        }
389                    } else {
390                        return Some((FieldItem::new(field), peek));
391                    }
392                }
393            }
394        }
395    }
396}