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
233                        if let Ok(key_str) = key_peek.get::<String>() {
234                            let field_item = FieldItem::flattened_map_entry(key_str.clone());
235                            return Some((field_item, value_peek));
236                        }
237                        // Skip entries with non-string keys
238                        continue;
239                    }
240                    // Map exhausted, continue to next state
241                    continue;
242                }
243                FieldsForSerializeIterState::Fields(mut fields) => {
244                    let Some((field, peek)) = fields.next() else {
245                        continue;
246                    };
247                    self.stack.push(FieldsForSerializeIterState::Fields(fields));
248
249                    let data = peek.data();
250                    let should_skip = unsafe { field.should_skip_serializing(data) };
251
252                    if should_skip {
253                        continue;
254                    }
255
256                    if field.is_flattened() {
257                        if let Ok(struct_peek) = peek.into_struct() {
258                            self.stack.push(FieldsForSerializeIterState::Fields(
259                                FieldIter::new_struct(struct_peek),
260                            ))
261                        } else if let Ok(enum_peek) = peek.into_enum() {
262                            // normally we'd serialize to something like:
263                            //
264                            //   {
265                            //     "field_on_struct": {
266                            //       "VariantName": { "field_on_variant": "foo" }
267                            //     }
268                            //   }
269                            //
270                            // But since `field_on_struct` is flattened, instead we do:
271                            //
272                            //   {
273                            //     "VariantName": { "field_on_variant": "foo" }
274                            //   }
275                            let variant_name = enum_peek
276                                .active_variant()
277                                .expect("Failed to get active variant")
278                                .name;
279                            let field_item = FieldItem::flattened_enum(field, variant_name);
280                            self.stack.push(FieldsForSerializeIterState::FlattenedEnum {
281                                field_item: Some(field_item),
282                                value: peek,
283                            });
284                        } else if let Ok(map_peek) = peek.into_map() {
285                            // Flattened map - emit entries as synthetic fields
286                            self.stack.push(FieldsForSerializeIterState::FlattenedMap {
287                                map_iter: map_peek.iter(),
288                            });
289                        } else if let Ok(option_peek) = peek.into_option() {
290                            // Option<T> where T is a struct, enum, or map
291                            // If Some, flatten the inner value; if None, skip entirely
292                            if let Some(inner_peek) = option_peek.value() {
293                                if let Ok(struct_peek) = inner_peek.into_struct() {
294                                    self.stack.push(FieldsForSerializeIterState::Fields(
295                                        FieldIter::new_struct(struct_peek),
296                                    ))
297                                } else if let Ok(enum_peek) = inner_peek.into_enum() {
298                                    let variant_name = enum_peek
299                                        .active_variant()
300                                        .expect("Failed to get active variant")
301                                        .name;
302                                    let field_item = FieldItem::flattened_enum(field, variant_name);
303                                    self.stack.push(FieldsForSerializeIterState::FlattenedEnum {
304                                        field_item: Some(field_item),
305                                        value: inner_peek,
306                                    });
307                                } else if let Ok(map_peek) = inner_peek.into_map() {
308                                    self.stack.push(FieldsForSerializeIterState::FlattenedMap {
309                                        map_iter: map_peek.iter(),
310                                    });
311                                } else {
312                                    panic!(
313                                        "cannot flatten Option<{}> - inner type must be struct, enum, or map",
314                                        inner_peek.shape()
315                                    )
316                                }
317                            }
318                            // If None, we just skip - don't emit any fields
319                        } else {
320                            // TODO: fail more gracefully
321                            panic!("cannot flatten a {}", field.shape())
322                        }
323                    } else {
324                        return Some((FieldItem::new(field), peek));
325                    }
326                }
327            }
328        }
329    }
330}