facet_reflect/peek/
struct_.rs

1use facet_core::{Field, FieldError, FieldFlags, StructType};
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, 'shape> {
9    /// the underlying value
10    pub(crate) value: Peek<'mem, 'facet, 'shape>,
11
12    /// the definition of the struct!
13    pub(crate) ty: StructType<'shape>,
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, 'shape> PeekStruct<'mem, 'facet, 'shape> {
23    /// Returns the struct definition
24    #[inline(always)]
25    pub fn ty(&self) -> &StructType {
26        &self.ty
27    }
28
29    /// Returns the number of fields in this struct
30    #[inline(always)]
31    pub fn field_count(&self) -> usize {
32        self.ty.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, 'shape>, FieldError> {
38        self.ty
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                index,
47                bound: self.ty.fields.len(),
48            })
49    }
50
51    /// Gets the value of the field with the given name
52    #[inline]
53    pub fn field_by_name(&self, name: &str) -> Result<Peek<'mem, 'facet, 'shape>, FieldError> {
54        for (i, field) in self.ty.fields.iter().enumerate() {
55            if field.name == name {
56                return self.field(i);
57            }
58        }
59        Err(FieldError::NoSuchField)
60    }
61}
62
63impl<'mem, 'facet, 'shape> HasFields<'mem, 'facet, 'shape> for PeekStruct<'mem, 'facet, 'shape> {
64    /// Iterates over all fields in this struct, providing both name and value
65    #[inline]
66    fn fields(
67        &self,
68    ) -> impl DoubleEndedIterator<Item = (Field<'shape>, Peek<'mem, 'facet, 'shape>)> {
69        (0..self.field_count()).filter_map(|i| {
70            let field = self.ty.fields.get(i).copied()?;
71            let value = self.field(i).ok()?;
72            Some((field, value))
73        })
74    }
75}
76
77/// Trait for types that have field methods
78///
79/// This trait allows code to be written generically over both structs and enums
80/// that provide field access and iteration capabilities.
81pub trait HasFields<'mem, 'facet, 'shape> {
82    /// Iterates over all fields in this type, providing both field metadata and value
83    fn fields(
84        &self,
85    ) -> impl DoubleEndedIterator<Item = (Field<'shape>, Peek<'mem, 'facet, 'shape>)>;
86
87    /// Iterates over fields in this type that should be included when it is serialized
88    fn fields_for_serialize(
89        &self,
90    ) -> impl DoubleEndedIterator<Item = (Field<'shape>, Peek<'mem, 'facet, 'shape>)> {
91        // This is a default implementation that filters out fields with `skip_serializing`
92        // attribute and handles field flattening.
93        self.fields()
94            .filter(|(field, peek)| !unsafe { field.should_skip_serializing(peek.data()) })
95            .flat_map(move |(mut field, peek)| {
96                if field.flags.contains(FieldFlags::FLATTEN) {
97                    let mut flattened = Vec::new();
98                    if let Ok(struct_peek) = peek.into_struct() {
99                        struct_peek
100                            .fields_for_serialize()
101                            .for_each(|item| flattened.push(item));
102                    } else if let Ok(enum_peek) = peek.into_enum() {
103                        // normally we'd serialize to something like:
104                        //
105                        //   {
106                        //     "field_on_struct": {
107                        //       "VariantName": { "field_on_variant": "foo" }
108                        //     }
109                        //   }
110                        //
111                        // But since `field_on_struct` is flattened, instead we do:
112                        //
113                        //   {
114                        //     "VariantName": { "field_on_variant": "foo" }
115                        //   }
116                        field.name = enum_peek
117                            .active_variant()
118                            .expect("Failed to get active variant")
119                            .name;
120                        field.flattened = true;
121                        flattened.push((field, peek));
122                    } else {
123                        // TODO: fail more gracefully
124                        panic!("cannot flatten a {}", field.shape())
125                    }
126                    flattened
127                } else {
128                    vec![(field, peek)]
129                }
130            })
131    }
132}