facet_reflect/peek/
struct_.rs

1use facet_core::{Field, FieldError, FieldFlags, StructDef};
2
3use crate::Peek;
4use alloc::{vec, vec::Vec};
5
6/// Lets you read from a struct (implements read-only struct operations)
7#[derive(Clone, Copy)]
8pub struct PeekStruct<'mem, 'facet_lifetime> {
9    /// the underlying value
10    pub(crate) value: Peek<'mem, 'facet_lifetime>,
11
12    /// the definition of the struct!
13    pub(crate) def: StructDef,
14}
15
16impl core::fmt::Debug for PeekStruct<'_, '_> {
17    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
18        f.debug_struct("PeekStruct").finish_non_exhaustive()
19    }
20}
21
22impl<'mem, 'facet_lifetime> PeekStruct<'mem, 'facet_lifetime> {
23    /// Returns the struct definition
24    #[inline(always)]
25    pub fn def(&self) -> &StructDef {
26        &self.def
27    }
28
29    /// Returns the number of fields in this struct
30    #[inline(always)]
31    pub fn field_count(&self) -> usize {
32        self.def.fields.len()
33    }
34
35    /// Returns the value of the field at the given index
36    #[inline(always)]
37    pub fn field(&self, index: usize) -> Result<Peek<'mem, 'facet_lifetime>, FieldError> {
38        self.def
39            .fields
40            .get(index)
41            .map(|field| unsafe {
42                let field_data = self.value.data().field(field.offset);
43                Peek::unchecked_new(field_data, field.shape())
44            })
45            .ok_or(FieldError::IndexOutOfBounds)
46    }
47
48    /// Gets the value of the field with the given name
49    #[inline]
50    pub fn field_by_name(&self, name: &str) -> Result<Peek<'mem, 'facet_lifetime>, FieldError> {
51        for (i, field) in self.def.fields.iter().enumerate() {
52            if field.name == name {
53                return self.field(i);
54            }
55        }
56        Err(FieldError::NoSuchField)
57    }
58}
59
60impl<'mem, 'facet_lifetime> HasFields<'mem, 'facet_lifetime> for PeekStruct<'mem, 'facet_lifetime> {
61    fn fields(&self) -> impl DoubleEndedIterator<Item = (Field, Peek<'mem, 'facet_lifetime>)> {
62        (0..self.def.fields.len())
63            .filter_map(move |i| self.field(i).ok().map(|value| (self.def.fields[i], value)))
64    }
65}
66
67/// Trait for types that have field methods
68///
69/// This trait allows code to be written generically over both structs and enums
70/// that provide field access and iteration capabilities.
71pub trait HasFields<'mem, 'facet_lifetime> {
72    /// Iterates over all fields in this type, providing both field metadata and value
73    fn fields(&self) -> impl DoubleEndedIterator<Item = (Field, Peek<'mem, 'facet_lifetime>)>;
74
75    /// Iterates over fields in this type that should be included when it is serialized
76    fn fields_for_serialize(
77        &self,
78    ) -> impl DoubleEndedIterator<Item = (Field, Peek<'mem, 'facet_lifetime>)> {
79        // This is a default implementation that filters out fields with `skip_serializing`
80        // attribute and handles field flattening.
81        self.fields()
82            .filter(|(field, peek)| !unsafe { field.should_skip_serializing(peek.data()) })
83            .flat_map(|(mut field, peek)| {
84                if field.flags.contains(FieldFlags::FLATTEN) {
85                    let mut flattened = Vec::new();
86                    if let Ok(struct_peek) = peek.into_struct() {
87                        struct_peek
88                            .fields_for_serialize()
89                            .for_each(|item| flattened.push(item));
90                    } else if let Ok(enum_peek) = peek.into_enum() {
91                        // normally we'd serialize to something like:
92                        //
93                        //   {
94                        //     "field_on_struct": {
95                        //       "VariantName": { "field_on_variant": "foo" }
96                        //     }
97                        //   }
98                        //
99                        // But since `field_on_struct` is flattened, instead we do:
100                        //
101                        //   {
102                        //     "VariantName": { "field_on_variant": "foo" }
103                        //   }
104                        field.name = enum_peek.active_variant().name;
105                        field.flattened = true;
106                        flattened.push((field, peek));
107                    } else {
108                        // TODO: fail more gracefully
109                        panic!("cannot flatten a {}", field.shape())
110                    }
111                    flattened.into_iter()
112                } else {
113                    vec![(field, peek)].into_iter()
114                }
115            })
116    }
117}