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    pub(crate) fn new_struct(struct_: PeekStruct<'mem, 'facet, 'shape>) -> Self {
47        Self {
48            range: 0..struct_.ty.fields.len(),
49            state: FieldIterState::Struct(struct_),
50        }
51    }
52
53    pub(crate) fn new_enum(enum_: PeekEnum<'mem, 'facet, 'shape>) -> Self {
54        // Get the fields of the active variant
55        let variant = match enum_.active_variant() {
56            Ok(v) => v,
57            Err(e) => panic!("Cannot get active variant: {:?}", e),
58        };
59        let fields = &variant.data.fields;
60
61        Self {
62            range: 0..fields.len(),
63            state: FieldIterState::Enum {
64                peek_enum: enum_,
65                fields,
66            },
67        }
68    }
69
70    pub(crate) fn new_tuple(tuple: PeekTuple<'mem, 'facet, 'shape>) -> Self {
71        Self {
72            range: 0..tuple.len(),
73            state: FieldIterState::Tuple(tuple),
74        }
75    }
76
77    fn get_field_by_index(
78        &self,
79        index: usize,
80    ) -> Option<(Field<'shape>, Peek<'mem, 'facet, 'shape>)> {
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, 'shape> Iterator for FieldIter<'mem, 'facet, 'shape> {
116    type Item = (Field<'shape>, Peek<'mem, 'facet, 'shape>);
117
118    fn next(&mut self) -> Option<Self::Item> {
119        loop {
120            let index = self.range.next()?;
121
122            let Some(field) = self.get_field_by_index(index) else {
123                continue;
124            };
125
126            return Some(field);
127        }
128    }
129
130    fn size_hint(&self) -> (usize, Option<usize>) {
131        self.range.size_hint()
132    }
133}
134
135impl DoubleEndedIterator for FieldIter<'_, '_, '_> {
136    fn next_back(&mut self) -> Option<Self::Item> {
137        loop {
138            let index = self.range.next_back()?;
139
140            let Some(field) = self.get_field_by_index(index) else {
141                continue;
142            };
143
144            return Some(field);
145        }
146    }
147}
148
149impl ExactSizeIterator for FieldIter<'_, '_, '_> {}
150
151/// An iterator over the fields of a struct or enum that should be serialized. See [`HasFields::fields_for_serialize`]
152pub struct FieldsForSerializeIter<'mem, 'facet, 'shape> {
153    stack: Vec<FieldIter<'mem, 'facet, 'shape>>,
154}
155
156impl<'mem, 'facet, 'shape> Iterator for FieldsForSerializeIter<'mem, 'facet, 'shape> {
157    type Item = (Field<'shape>, Peek<'mem, 'facet, 'shape>);
158
159    fn next(&mut self) -> Option<Self::Item> {
160        loop {
161            let mut fields = self.stack.pop()?;
162            let Some((mut field, peek)) = fields.next() else {
163                continue;
164            };
165            self.stack.push(fields);
166
167            let Some(data) = peek.data().thin() else {
168                continue;
169            };
170            let should_skip = unsafe { field.should_skip_serializing(data) };
171
172            if should_skip {
173                continue;
174            }
175
176            if field.flags.contains(FieldFlags::FLATTEN) && !field.flattened {
177                if let Ok(struct_peek) = peek.into_struct() {
178                    self.stack.push(FieldIter::new_struct(struct_peek))
179                } else if let Ok(enum_peek) = peek.into_enum() {
180                    // normally we'd serialize to something like:
181                    //
182                    //   {
183                    //     "field_on_struct": {
184                    //       "VariantName": { "field_on_variant": "foo" }
185                    //     }
186                    //   }
187                    //
188                    // But since `field_on_struct` is flattened, instead we do:
189                    //
190                    //   {
191                    //     "VariantName": { "field_on_variant": "foo" }
192                    //   }
193                    field.name = enum_peek
194                        .active_variant()
195                        .expect("Failed to get active variant")
196                        .name;
197                    field.flattened = true;
198                    self.stack.push(FieldIter {
199                        range: 0..1,
200                        state: FieldIterState::FlattenedEnum { field, value: peek },
201                    });
202                } else {
203                    // TODO: fail more gracefully
204                    panic!("cannot flatten a {}", field.shape())
205                }
206            } else {
207                return Some((field, peek));
208            }
209        }
210    }
211}