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, 'shape> {
15    /// Iterates over all fields in this type, providing both field metadata and value
16    fn fields(&self) -> FieldIter<'mem, 'facet, 'shape>;
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, 'shape> {
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, 'shape> {
28    state: FieldIterState<'mem, 'facet, 'shape>,
29    range: Range<usize>,
30}
31
32enum FieldIterState<'mem, 'facet, 'shape> {
33    Struct(PeekStruct<'mem, 'facet, 'shape>),
34    Tuple(PeekTuple<'mem, 'facet, 'shape>),
35    Enum {
36        peek_enum: PeekEnum<'mem, 'facet, 'shape>,
37        fields: &'shape [Field<'shape>],
38    },
39    FlattenedEnum {
40        field: Field<'shape>,
41        value: Peek<'mem, 'facet, 'shape>,
42    },
43}
44
45impl<'mem, 'facet, 'shape> FieldIter<'mem, 'facet, 'shape> {
46    #[inline]
47    pub(crate) fn new_struct(struct_: PeekStruct<'mem, 'facet, 'shape>) -> 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, 'shape>) -> 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, 'shape>) -> Self {
74        Self {
75            range: 0..tuple.len(),
76            state: FieldIterState::Tuple(tuple),
77        }
78    }
79
80    fn get_field_by_index(
81        &self,
82        index: usize,
83    ) -> Option<(Field<'shape>, Peek<'mem, 'facet, 'shape>)> {
84        match self.state {
85            FieldIterState::Struct(peek_struct) => {
86                let field = peek_struct.ty.fields.get(index).copied()?;
87                let value = peek_struct.field(index).ok()?;
88                Some((field, value))
89            }
90            FieldIterState::Tuple(peek_tuple) => {
91                let field = peek_tuple.ty.fields.get(index).copied()?;
92                let value = peek_tuple.field(index)?;
93                Some((field, value))
94            }
95            FieldIterState::Enum { peek_enum, fields } => {
96                // Get the field definition
97                let field = fields[index];
98                // Get the field value
99                let field_value = match peek_enum.field(index) {
100                    Ok(Some(v)) => v,
101                    Ok(None) => return None,
102                    Err(e) => panic!("Cannot get field: {e:?}"),
103                };
104                // Return the field definition and value
105                Some((field, field_value))
106            }
107            FieldIterState::FlattenedEnum { field, value } => {
108                if index == 0 {
109                    Some((field, value))
110                } else {
111                    None
112                }
113            }
114        }
115    }
116}
117
118impl<'mem, 'facet, 'shape> Iterator for FieldIter<'mem, 'facet, 'shape> {
119    type Item = (Field<'shape>, Peek<'mem, 'facet, 'shape>);
120
121    #[inline]
122    fn next(&mut self) -> Option<Self::Item> {
123        loop {
124            let index = self.range.next()?;
125
126            let Some(field) = self.get_field_by_index(index) else {
127                continue;
128            };
129
130            return Some(field);
131        }
132    }
133
134    #[inline]
135    fn size_hint(&self) -> (usize, Option<usize>) {
136        self.range.size_hint()
137    }
138}
139
140impl DoubleEndedIterator for FieldIter<'_, '_, '_> {
141    #[inline]
142    fn next_back(&mut self) -> Option<Self::Item> {
143        loop {
144            let index = self.range.next_back()?;
145
146            let Some(field) = self.get_field_by_index(index) else {
147                continue;
148            };
149
150            return Some(field);
151        }
152    }
153}
154
155impl ExactSizeIterator for FieldIter<'_, '_, '_> {}
156
157/// An iterator over the fields of a struct or enum that should be serialized. See [`HasFields::fields_for_serialize`]
158pub struct FieldsForSerializeIter<'mem, 'facet, 'shape> {
159    stack: Vec<FieldIter<'mem, 'facet, 'shape>>,
160}
161
162impl<'mem, 'facet, 'shape> Iterator for FieldsForSerializeIter<'mem, 'facet, 'shape> {
163    type Item = (Field<'shape>, Peek<'mem, 'facet, 'shape>);
164
165    fn next(&mut self) -> Option<Self::Item> {
166        loop {
167            let mut fields = self.stack.pop()?;
168            let Some((mut field, peek)) = fields.next() else {
169                continue;
170            };
171            self.stack.push(fields);
172
173            let Some(data) = peek.data().thin() else {
174                continue;
175            };
176            let should_skip = unsafe { field.should_skip_serializing(data) };
177
178            if should_skip {
179                continue;
180            }
181
182            if field.flags.contains(FieldFlags::FLATTEN) && !field.flattened {
183                if let Ok(struct_peek) = peek.into_struct() {
184                    self.stack.push(FieldIter::new_struct(struct_peek))
185                } else if let Ok(enum_peek) = peek.into_enum() {
186                    // normally we'd serialize to something like:
187                    //
188                    //   {
189                    //     "field_on_struct": {
190                    //       "VariantName": { "field_on_variant": "foo" }
191                    //     }
192                    //   }
193                    //
194                    // But since `field_on_struct` is flattened, instead we do:
195                    //
196                    //   {
197                    //     "VariantName": { "field_on_variant": "foo" }
198                    //   }
199                    field.name = enum_peek
200                        .active_variant()
201                        .expect("Failed to get active variant")
202                        .name;
203                    field.flattened = true;
204                    self.stack.push(FieldIter {
205                        range: 0..1,
206                        state: FieldIterState::FlattenedEnum { field, value: peek },
207                    });
208                } else {
209                    // TODO: fail more gracefully
210                    panic!("cannot flatten a {}", field.shape())
211                }
212            } else {
213                return Some((field, peek));
214            }
215        }
216    }
217}