Skip to main content

facet_reflect/poke/
struct_.rs

1use facet_core::{Facet, FieldError, StructType};
2use facet_path::Path;
3
4use crate::{ReflectError, ReflectErrorKind};
5
6use super::Poke;
7
8/// Lets you mutate a struct's fields.
9pub struct PokeStruct<'mem, 'facet> {
10    /// The underlying value
11    pub(crate) value: Poke<'mem, 'facet>,
12
13    /// The definition of the struct
14    pub(crate) ty: StructType,
15}
16
17impl core::fmt::Debug for PokeStruct<'_, '_> {
18    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
19        f.debug_struct("PokeStruct").finish_non_exhaustive()
20    }
21}
22
23impl<'mem, 'facet> PokeStruct<'mem, 'facet> {
24    fn err(&self, kind: ReflectErrorKind) -> ReflectError {
25        ReflectError::new(kind, Path::new(self.value.shape))
26    }
27
28    /// Returns the struct definition.
29    #[inline(always)]
30    pub const fn ty(&self) -> &StructType {
31        &self.ty
32    }
33
34    /// Returns the number of fields in this struct.
35    #[inline(always)]
36    pub const fn field_count(&self) -> usize {
37        self.ty.fields.len()
38    }
39
40    /// Returns a `Poke` for the field at the given index.
41    ///
42    /// This always succeeds (for valid indices). The POD check happens when
43    /// you try to mutate via [`Poke::set`] on the returned field poke, or
44    /// when calling [`PokeStruct::set_field`] which checks the parent struct.
45    pub fn field(&mut self, index: usize) -> Result<Poke<'_, 'facet>, ReflectError> {
46        let field = self.ty.fields.get(index).ok_or_else(|| {
47            self.err(ReflectErrorKind::FieldError {
48                shape: self.value.shape,
49                field_error: FieldError::IndexOutOfBounds {
50                    index,
51                    bound: self.ty.fields.len(),
52                },
53            })
54        })?;
55
56        let field_data = unsafe { self.value.data.field(field.offset) };
57        let field_shape = field.shape();
58
59        Ok(unsafe { Poke::from_raw_parts(field_data, field_shape) })
60    }
61
62    /// Returns a `Poke` for the field with the given name.
63    ///
64    /// Returns an error if the field is not found.
65    pub fn field_by_name(&mut self, name: &str) -> Result<Poke<'_, 'facet>, ReflectError> {
66        for (i, field) in self.ty.fields.iter().enumerate() {
67            if field.name == name {
68                return self.field(i);
69            }
70        }
71        Err(self.err(ReflectErrorKind::FieldError {
72            shape: self.value.shape,
73            field_error: FieldError::NoSuchField,
74        }))
75    }
76
77    /// Sets the value of a field by index.
78    ///
79    /// The value type must match the field's type.
80    ///
81    /// Returns an error if the parent struct is not POD. Field mutation could
82    /// violate struct-level invariants, so the struct must be marked with
83    /// `#[facet(pod)]` to allow this.
84    pub fn set_field<T: Facet<'facet>>(
85        &mut self,
86        index: usize,
87        value: T,
88    ) -> Result<(), ReflectError> {
89        // Check that the parent struct is POD before allowing field mutation
90        if !self.value.shape.is_pod() {
91            return Err(self.err(ReflectErrorKind::NotPod {
92                shape: self.value.shape,
93            }));
94        }
95
96        let field = self.ty.fields.get(index).ok_or_else(|| {
97            self.err(ReflectErrorKind::FieldError {
98                shape: self.value.shape,
99                field_error: FieldError::IndexOutOfBounds {
100                    index,
101                    bound: self.ty.fields.len(),
102                },
103            })
104        })?;
105
106        let field_shape = field.shape();
107        if field_shape != T::SHAPE {
108            return Err(self.err(ReflectErrorKind::WrongShape {
109                expected: field_shape,
110                actual: T::SHAPE,
111            }));
112        }
113
114        unsafe {
115            let field_ptr = self.value.data.field(field.offset);
116            // Drop the old value and write the new one
117            field_shape.call_drop_in_place(field_ptr);
118            core::ptr::write(field_ptr.as_mut_byte_ptr() as *mut T, value);
119        }
120
121        Ok(())
122    }
123
124    /// Sets the value of a field by name.
125    ///
126    /// The value type must match the field's type.
127    pub fn set_field_by_name<T: Facet<'facet>>(
128        &mut self,
129        name: &str,
130        value: T,
131    ) -> Result<(), ReflectError> {
132        for (i, field) in self.ty.fields.iter().enumerate() {
133            if field.name == name {
134                return self.set_field(i, value);
135            }
136        }
137        Err(self.err(ReflectErrorKind::FieldError {
138            shape: self.value.shape,
139            field_error: FieldError::NoSuchField,
140        }))
141    }
142
143    /// Gets a read-only view of a field by index.
144    pub fn peek_field(&self, index: usize) -> Result<crate::Peek<'_, 'facet>, FieldError> {
145        let field = self
146            .ty
147            .fields
148            .get(index)
149            .ok_or(FieldError::IndexOutOfBounds {
150                index,
151                bound: self.ty.fields.len(),
152            })?;
153
154        let field_data = unsafe { self.value.data.as_const().field(field.offset) };
155        Ok(unsafe { crate::Peek::unchecked_new(field_data, field.shape()) })
156    }
157
158    /// Gets a read-only view of a field by name.
159    pub fn peek_field_by_name(&self, name: &str) -> Result<crate::Peek<'_, 'facet>, FieldError> {
160        for (i, field) in self.ty.fields.iter().enumerate() {
161            if field.name == name {
162                return self.peek_field(i);
163            }
164        }
165        Err(FieldError::NoSuchField)
166    }
167
168    /// Converts this back into the underlying `Poke`.
169    #[inline]
170    pub const fn into_inner(self) -> Poke<'mem, 'facet> {
171        self.value
172    }
173
174    /// Returns a read-only `PeekStruct` view.
175    #[inline]
176    pub fn as_peek_struct(&self) -> crate::PeekStruct<'_, 'facet> {
177        crate::PeekStruct {
178            value: self.value.as_peek(),
179            ty: self.ty,
180        }
181    }
182}
183
184// Note: PokeStruct tests require custom structs with #[derive(Facet)] which can't be done
185// in inline tests within facet-reflect. All PokeStruct tests are in the integration tests:
186// facet-reflect/tests/poke/struct_.rs