facet_reflect/peek/
fields.rs

1use core::ops::Range;
2
3use facet_core::Field;
4
5use crate::Peek;
6use alloc::{vec, vec::Vec};
7
8use super::{PeekEnum, PeekStruct, PeekTuple};
9
10/// A field item with runtime state for serialization.
11///
12/// This wraps a static `Field` with additional runtime state that can be modified
13/// during iteration (e.g., for flattened enums where the field name becomes the variant name).
14#[derive(Clone, Copy, Debug)]
15pub struct FieldItem {
16    /// The underlying static field definition
17    pub field: Field,
18    /// Runtime-determined name (may differ from field.name for flattened enums)
19    pub name: &'static str,
20    /// Whether this field was flattened from an enum (variant name used as key)
21    pub flattened: bool,
22}
23
24impl FieldItem {
25    /// Create a new FieldItem from a Field, using the field's name
26    #[inline]
27    pub fn new(field: Field) -> Self {
28        Self {
29            name: field.name,
30            field,
31            flattened: false,
32        }
33    }
34
35    /// Create a flattened enum field item with a custom name (the variant name)
36    #[inline]
37    pub fn flattened_enum(field: Field, variant_name: &'static str) -> Self {
38        Self {
39            name: variant_name,
40            field,
41            flattened: true,
42        }
43    }
44}
45
46/// Trait for types that have field methods
47///
48/// This trait allows code to be written generically over both structs and enums
49/// that provide field access and iteration capabilities.
50pub trait HasFields<'mem, 'facet> {
51    /// Iterates over all fields in this type, providing both field metadata and value
52    fn fields(&self) -> FieldIter<'mem, 'facet>;
53
54    /// Iterates over fields in this type that should be included when it is serialized
55    fn fields_for_serialize(&self) -> FieldsForSerializeIter<'mem, 'facet> {
56        FieldsForSerializeIter {
57            stack: vec![FieldsForSerializeIterState::Fields(self.fields())],
58        }
59    }
60}
61
62/// An iterator over all the fields of a struct or enum. See [`HasFields::fields`]
63pub struct FieldIter<'mem, 'facet> {
64    state: FieldIterState<'mem, 'facet>,
65    range: Range<usize>,
66}
67
68enum FieldIterState<'mem, 'facet> {
69    Struct(PeekStruct<'mem, 'facet>),
70    Tuple(PeekTuple<'mem, 'facet>),
71    Enum {
72        peek_enum: PeekEnum<'mem, 'facet>,
73        fields: &'static [Field],
74    },
75}
76
77impl<'mem, 'facet> FieldIter<'mem, 'facet> {
78    #[inline]
79    pub(crate) fn new_struct(struct_: PeekStruct<'mem, 'facet>) -> Self {
80        Self {
81            range: 0..struct_.ty.fields.len(),
82            state: FieldIterState::Struct(struct_),
83        }
84    }
85
86    #[inline]
87    pub(crate) fn new_enum(enum_: PeekEnum<'mem, 'facet>) -> Self {
88        // Get the fields of the active variant
89        let variant = match enum_.active_variant() {
90            Ok(v) => v,
91            Err(e) => panic!("Cannot get active variant: {e:?}"),
92        };
93        let fields = &variant.data.fields;
94
95        Self {
96            range: 0..fields.len(),
97            state: FieldIterState::Enum {
98                peek_enum: enum_,
99                fields,
100            },
101        }
102    }
103
104    #[inline]
105    pub(crate) fn new_tuple(tuple: PeekTuple<'mem, 'facet>) -> Self {
106        Self {
107            range: 0..tuple.len(),
108            state: FieldIterState::Tuple(tuple),
109        }
110    }
111
112    fn get_field_by_index(&self, index: usize) -> Option<(Field, Peek<'mem, 'facet>)> {
113        match self.state {
114            FieldIterState::Struct(peek_struct) => {
115                let field = peek_struct.ty.fields.get(index).copied()?;
116                let value = peek_struct.field(index).ok()?;
117                Some((field, value))
118            }
119            FieldIterState::Tuple(peek_tuple) => {
120                let field = peek_tuple.ty.fields.get(index).copied()?;
121                let value = peek_tuple.field(index)?;
122                Some((field, value))
123            }
124            FieldIterState::Enum { peek_enum, fields } => {
125                // Get the field definition
126                let field = fields[index];
127                // Get the field value
128                let field_value = match peek_enum.field(index) {
129                    Ok(Some(v)) => v,
130                    Ok(None) => return None,
131                    Err(e) => panic!("Cannot get field: {e:?}"),
132                };
133                // Return the field definition and value
134                Some((field, field_value))
135            }
136        }
137    }
138}
139
140impl<'mem, 'facet> Iterator for FieldIter<'mem, 'facet> {
141    type Item = (Field, Peek<'mem, 'facet>);
142
143    #[inline]
144    fn next(&mut self) -> Option<Self::Item> {
145        loop {
146            let index = self.range.next()?;
147
148            let Some(field) = self.get_field_by_index(index) else {
149                continue;
150            };
151
152            return Some(field);
153        }
154    }
155
156    #[inline]
157    fn size_hint(&self) -> (usize, Option<usize>) {
158        self.range.size_hint()
159    }
160}
161
162impl DoubleEndedIterator for FieldIter<'_, '_> {
163    #[inline]
164    fn next_back(&mut self) -> Option<Self::Item> {
165        loop {
166            let index = self.range.next_back()?;
167
168            let Some(field) = self.get_field_by_index(index) else {
169                continue;
170            };
171
172            return Some(field);
173        }
174    }
175}
176
177impl ExactSizeIterator for FieldIter<'_, '_> {}
178
179/// An iterator over the fields of a struct or enum that should be serialized. See [`HasFields::fields_for_serialize`]
180pub struct FieldsForSerializeIter<'mem, 'facet> {
181    stack: Vec<FieldsForSerializeIterState<'mem, 'facet>>,
182}
183
184enum FieldsForSerializeIterState<'mem, 'facet> {
185    /// Normal field iteration
186    Fields(FieldIter<'mem, 'facet>),
187    /// A single flattened enum item to yield
188    FlattenedEnum {
189        field_item: Option<FieldItem>,
190        value: Peek<'mem, 'facet>,
191    },
192}
193
194impl<'mem, 'facet> Iterator for FieldsForSerializeIter<'mem, 'facet> {
195    type Item = (FieldItem, Peek<'mem, 'facet>);
196
197    fn next(&mut self) -> Option<Self::Item> {
198        loop {
199            let state = self.stack.pop()?;
200
201            match state {
202                FieldsForSerializeIterState::FlattenedEnum { field_item, value } => {
203                    // Yield the flattened enum item (only once)
204                    if let Some(item) = field_item {
205                        return Some((item, value));
206                    }
207                    // Already yielded, continue to next state
208                    continue;
209                }
210                FieldsForSerializeIterState::Fields(mut fields) => {
211                    let Some((field, peek)) = fields.next() else {
212                        continue;
213                    };
214                    self.stack.push(FieldsForSerializeIterState::Fields(fields));
215
216                    let data = peek.data();
217                    let should_skip = unsafe { field.should_skip_serializing(data) };
218
219                    if should_skip {
220                        continue;
221                    }
222
223                    if field.is_flattened() {
224                        if let Ok(struct_peek) = peek.into_struct() {
225                            self.stack.push(FieldsForSerializeIterState::Fields(
226                                FieldIter::new_struct(struct_peek),
227                            ))
228                        } else if let Ok(enum_peek) = peek.into_enum() {
229                            // normally we'd serialize to something like:
230                            //
231                            //   {
232                            //     "field_on_struct": {
233                            //       "VariantName": { "field_on_variant": "foo" }
234                            //     }
235                            //   }
236                            //
237                            // But since `field_on_struct` is flattened, instead we do:
238                            //
239                            //   {
240                            //     "VariantName": { "field_on_variant": "foo" }
241                            //   }
242                            let variant_name = enum_peek
243                                .active_variant()
244                                .expect("Failed to get active variant")
245                                .name;
246                            let field_item = FieldItem::flattened_enum(field, variant_name);
247                            self.stack.push(FieldsForSerializeIterState::FlattenedEnum {
248                                field_item: Some(field_item),
249                                value: peek,
250                            });
251                        } else if let Ok(option_peek) = peek.into_option() {
252                            // Option<T> where T is a struct or enum
253                            // If Some, flatten the inner value; if None, skip entirely
254                            if let Some(inner_peek) = option_peek.value() {
255                                if let Ok(struct_peek) = inner_peek.into_struct() {
256                                    self.stack.push(FieldsForSerializeIterState::Fields(
257                                        FieldIter::new_struct(struct_peek),
258                                    ))
259                                } else if let Ok(enum_peek) = inner_peek.into_enum() {
260                                    let variant_name = enum_peek
261                                        .active_variant()
262                                        .expect("Failed to get active variant")
263                                        .name;
264                                    let field_item = FieldItem::flattened_enum(field, variant_name);
265                                    self.stack.push(FieldsForSerializeIterState::FlattenedEnum {
266                                        field_item: Some(field_item),
267                                        value: inner_peek,
268                                    });
269                                } else {
270                                    panic!(
271                                        "cannot flatten Option<{}> - inner type must be struct or enum",
272                                        inner_peek.shape()
273                                    )
274                                }
275                            }
276                            // If None, we just skip - don't emit any fields
277                        } else {
278                            // TODO: fail more gracefully
279                            panic!("cannot flatten a {}", field.shape())
280                        }
281                    } else {
282                        return Some((FieldItem::new(field), peek));
283                    }
284                }
285            }
286        }
287    }
288}