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 using Display trait
233 // This works for String, SmolStr, SmartString, CompactString, etc.
234 if key_peek.shape().vtable.has_display() {
235 use alloc::string::ToString;
236 let key_str = key_peek.to_string();
237 let field_item = FieldItem::flattened_map_entry(key_str);
238 return Some((field_item, value_peek));
239 }
240 // Skip entries with non-string-like keys
241 continue;
242 }
243 // Map exhausted, continue to next state
244 continue;
245 }
246 FieldsForSerializeIterState::Fields(mut fields) => {
247 let Some((field, peek)) = fields.next() else {
248 continue;
249 };
250 self.stack.push(FieldsForSerializeIterState::Fields(fields));
251
252 let data = peek.data();
253 let should_skip = unsafe { field.should_skip_serializing(data) };
254
255 if should_skip {
256 continue;
257 }
258
259 if field.is_flattened() {
260 if let Ok(struct_peek) = peek.into_struct() {
261 self.stack.push(FieldsForSerializeIterState::Fields(
262 FieldIter::new_struct(struct_peek),
263 ))
264 } else if let Ok(enum_peek) = peek.into_enum() {
265 // normally we'd serialize to something like:
266 //
267 // {
268 // "field_on_struct": {
269 // "VariantName": { "field_on_variant": "foo" }
270 // }
271 // }
272 //
273 // But since `field_on_struct` is flattened, instead we do:
274 //
275 // {
276 // "VariantName": { "field_on_variant": "foo" }
277 // }
278 //
279 // To achieve this, we emit the variant name as the field key
280 // and the variant's inner value (not the whole enum) as the value.
281 let variant = enum_peek
282 .active_variant()
283 .expect("Failed to get active variant");
284 let field_item = FieldItem::flattened_enum(field, variant.name);
285
286 // Get the inner value based on variant kind
287 use facet_core::StructKind;
288 let inner_value = match variant.data.kind {
289 StructKind::Unit => {
290 // Unit variants have no inner value - skip
291 continue;
292 }
293 StructKind::TupleStruct | StructKind::Tuple
294 if variant.data.fields.len() == 1 =>
295 {
296 // Newtype variant - yield the inner value directly
297 enum_peek
298 .field(0)
299 .expect("Failed to get variant field")
300 .expect("Newtype variant should have field 0")
301 }
302 StructKind::TupleStruct
303 | StructKind::Tuple
304 | StructKind::Struct => {
305 // Multi-field tuple or struct variant - push fields iterator
306 self.stack.push(FieldsForSerializeIterState::FlattenedEnum {
307 field_item: Some(field_item),
308 value: peek,
309 });
310 // The serializer will handle enum variant serialization
311 // which will emit the variant's fields/array properly
312 continue;
313 }
314 };
315
316 self.stack.push(FieldsForSerializeIterState::FlattenedEnum {
317 field_item: Some(field_item),
318 value: inner_value,
319 });
320 } else if let Ok(map_peek) = peek.into_map() {
321 // Flattened map - emit entries as synthetic fields
322 self.stack.push(FieldsForSerializeIterState::FlattenedMap {
323 map_iter: map_peek.iter(),
324 });
325 } else if let Ok(option_peek) = peek.into_option() {
326 // Option<T> where T is a struct, enum, or map
327 // If Some, flatten the inner value; if None, skip entirely
328 if let Some(inner_peek) = option_peek.value() {
329 if let Ok(struct_peek) = inner_peek.into_struct() {
330 self.stack.push(FieldsForSerializeIterState::Fields(
331 FieldIter::new_struct(struct_peek),
332 ))
333 } else if let Ok(enum_peek) = inner_peek.into_enum() {
334 let variant = enum_peek
335 .active_variant()
336 .expect("Failed to get active variant");
337 let field_item = FieldItem::flattened_enum(field, variant.name);
338
339 // Get the inner value based on variant kind
340 use facet_core::StructKind;
341 let inner_value = match variant.data.kind {
342 StructKind::Unit => {
343 // Unit variants have no inner value - skip
344 continue;
345 }
346 StructKind::TupleStruct | StructKind::Tuple
347 if variant.data.fields.len() == 1 =>
348 {
349 // Newtype variant - yield the inner value directly
350 enum_peek
351 .field(0)
352 .expect("Failed to get variant field")
353 .expect("Newtype variant should have field 0")
354 }
355 StructKind::TupleStruct
356 | StructKind::Tuple
357 | StructKind::Struct => {
358 // Multi-field tuple or struct variant
359 self.stack.push(
360 FieldsForSerializeIterState::FlattenedEnum {
361 field_item: Some(field_item),
362 value: inner_peek,
363 },
364 );
365 continue;
366 }
367 };
368
369 self.stack.push(FieldsForSerializeIterState::FlattenedEnum {
370 field_item: Some(field_item),
371 value: inner_value,
372 });
373 } else if let Ok(map_peek) = inner_peek.into_map() {
374 self.stack.push(FieldsForSerializeIterState::FlattenedMap {
375 map_iter: map_peek.iter(),
376 });
377 } else {
378 panic!(
379 "cannot flatten Option<{}> - inner type must be struct, enum, or map",
380 inner_peek.shape()
381 )
382 }
383 }
384 // If None, we just skip - don't emit any fields
385 } else {
386 // TODO: fail more gracefully
387 panic!("cannot flatten a {}", field.shape())
388 }
389 } else {
390 return Some((FieldItem::new(field), peek));
391 }
392 }
393 }
394 }
395 }
396}