facet_reflect/peek/
fields.rs

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