facet_reflect/poke/
struct_.rs

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