facet_reflect/poke/
struct_.rs

1use core::ptr::NonNull;
2use facet_core::{Facet, FieldError, Opaque, OpaqueConst, OpaqueUninit, Shape, StructDef};
3
4#[cfg(feature = "alloc")]
5extern crate alloc;
6#[cfg(feature = "alloc")]
7use alloc::boxed::Box;
8
9use super::{Guard, ISet, PokeValueUninit};
10
11/// Allows poking a struct (setting fields, etc.)
12pub struct PokeStruct<'mem> {
13    data: OpaqueUninit<'mem>,
14    shape: &'static Shape,
15    def: StructDef,
16    iset: ISet,
17}
18
19impl<'mem> PokeStruct<'mem> {
20    #[inline(always)]
21    /// Coerce back into a `PokeValue`
22    pub fn into_value(self) -> PokeValueUninit<'mem> {
23        unsafe { PokeValueUninit::new(self.data, self.shape) }
24    }
25
26    /// Shape getter
27    #[inline(always)]
28    pub fn shape(&self) -> &'static Shape {
29        self.shape
30    }
31    /// Creates a new PokeStruct
32    ///
33    /// # Safety
34    ///
35    /// The `data`, `shape`, and `def` must match
36    pub unsafe fn new(data: OpaqueUninit<'mem>, shape: &'static Shape, def: StructDef) -> Self {
37        Self {
38            data,
39            iset: Default::default(),
40            shape,
41            def,
42        }
43    }
44
45    /// Checks if all fields in the struct have been initialized.
46    /// Panics if any field is not initialized, providing details about the uninitialized field.
47    pub fn assert_all_fields_initialized(&self) {
48        for (i, field) in self.def.fields.iter().enumerate() {
49            if !self.iset.has(i) {
50                panic!(
51                    "Field '{}' was not initialized. Complete schema:\n{:?}",
52                    field.name, self.shape
53                );
54            }
55        }
56    }
57
58    /// Asserts that every field has been initialized and forgets the PokeStruct.
59    ///
60    /// This method is only used when the origin is borrowed.
61    /// If this method is not called, all fields will be freed when the PokeStruct is dropped.
62    ///
63    /// # Panics
64    ///
65    /// This function will panic if any field is not initialized.
66    pub fn build_in_place(self) -> Opaque<'mem> {
67        // ensure all fields are initialized
68        self.assert_all_fields_initialized();
69
70        let data = unsafe { self.data.assume_init() };
71
72        // prevent field drops when the PokeStruct is dropped
73        core::mem::forget(self);
74
75        data
76    }
77
78    /// Builds a value of type `T` from the PokeStruct, then deallocates the memory
79    /// that this PokeStruct was pointing to.
80    ///
81    /// # Panics
82    ///
83    /// This function will panic if:
84    /// - Not all the fields have been initialized.
85    /// - The generic type parameter T does not match the shape that this PokeStruct is building.
86    pub fn build<T: Facet>(self, guard: Option<Guard>) -> T {
87        let mut guard = guard;
88        let this = self;
89        // this changes drop order: guard must be dropped _after_ this.
90
91        this.assert_all_fields_initialized();
92        this.shape.assert_type::<T>();
93        if let Some(guard) = &guard {
94            guard.shape.assert_type::<T>();
95        }
96
97        let result = unsafe {
98            let ptr = this.data.as_mut_bytes() as *const T;
99            core::ptr::read(ptr)
100        };
101        guard.take(); // dealloc
102        core::mem::forget(this);
103        result
104    }
105
106    /// Build that PokeStruct into a boxed completed shape.
107    ///
108    /// # Panics
109    ///
110    /// This function will panic if:
111    /// - Not all the fields have been initialized.
112    /// - The generic type parameter T does not match the shape that this PokeStruct is building.
113    #[cfg(feature = "alloc")]
114    pub fn build_boxed<T: Facet>(self) -> Box<T> {
115        self.assert_all_fields_initialized();
116        self.shape.assert_type::<T>();
117
118        let boxed = unsafe { Box::from_raw(self.data.as_mut_bytes() as *mut T) };
119        core::mem::forget(self);
120        boxed
121    }
122
123    /// Moves the contents of this `PokeStruct` into a target memory location.
124    ///
125    /// # Safety
126    ///
127    /// The target pointer must be valid and properly aligned,
128    /// and must be large enough to hold the value.
129    /// The caller is responsible for ensuring that the target memory is properly deallocated
130    /// when it's no longer needed.
131    pub unsafe fn move_into(self, target: NonNull<u8>, guard: Option<Guard>) {
132        self.assert_all_fields_initialized();
133        if let Some(guard) = &guard {
134            guard.shape.assert_shape(self.shape);
135        }
136
137        unsafe {
138            core::ptr::copy_nonoverlapping(
139                self.data.as_mut_bytes(),
140                target.as_ptr(),
141                self.shape.layout.size(),
142            );
143        }
144        core::mem::forget(self);
145    }
146
147    /// Gets a field, by name
148    pub fn field_by_name(
149        &self,
150        name: &str,
151    ) -> Result<(usize, crate::PokeUninit<'mem>), FieldError> {
152        let index = self
153            .def
154            .fields
155            .iter()
156            .position(|f| f.name == name)
157            .ok_or(FieldError::NoSuchStaticField)?;
158        Ok((index, self.field(index)?))
159    }
160
161    /// Get a field writer for a field by index.
162    ///
163    /// # Errors
164    ///
165    /// Returns an error if:
166    /// - The shape doesn't represent a struct.
167    /// - The index is out of bounds.
168    pub fn field(&self, index: usize) -> Result<crate::PokeUninit<'mem>, FieldError> {
169        if index >= self.def.fields.len() {
170            return Err(FieldError::IndexOutOfBounds);
171        }
172
173        let field = &self.def.fields[index];
174
175        // Get the field's address
176        let field_addr = unsafe { self.data.field_uninit(field.offset) };
177        let field_shape = field.shape;
178
179        let poke = unsafe { crate::PokeUninit::unchecked_new(field_addr, field_shape) };
180        Ok(poke)
181    }
182
183    /// Sets a field's value by its index, directly copying raw memory.
184    ///
185    /// # Safety
186    ///
187    /// This is unsafe because it directly copies memory without checking types.
188    /// The caller must ensure that `value` is of the correct type for the field.
189    ///
190    /// # Errors
191    ///
192    /// Returns an error if:
193    /// - The index is out of bounds
194    /// - The field shapes don't match
195    pub unsafe fn unchecked_set(
196        &mut self,
197        index: usize,
198        value: OpaqueConst,
199    ) -> Result<(), FieldError> {
200        if index >= self.def.fields.len() {
201            return Err(FieldError::IndexOutOfBounds);
202        }
203        let field = &self.def.fields[index];
204        let field_shape = field.shape;
205
206        unsafe {
207            core::ptr::copy_nonoverlapping(
208                value.as_ptr(),
209                self.data.field_uninit(field.offset).as_mut_bytes(),
210                field_shape.layout.size(),
211            );
212            self.iset.set(index);
213        }
214
215        Ok(())
216    }
217
218    /// Sets a field's value by its name, directly copying raw memory.
219    ///
220    /// # Safety
221    ///
222    /// This is unsafe because it directly copies memory without checking types.
223    /// The caller must ensure that `value` is of the correct type for the field.
224    ///
225    /// # Errors
226    ///
227    /// Returns an error if:
228    /// - The field name doesn't exist
229    /// - The field shapes don't match
230    pub unsafe fn unchecked_set_by_name(
231        &mut self,
232        name: &str,
233        value: OpaqueConst,
234    ) -> Result<(), FieldError> {
235        let index = self
236            .def
237            .fields
238            .iter()
239            .position(|f| f.name == name)
240            .ok_or(FieldError::NoSuchStaticField)?;
241        unsafe { self.unchecked_set(index, value) }
242    }
243
244    /// Sets a field's value by its index in a type-safe manner.
245    ///
246    /// This method takes ownership of the value and ensures proper memory management.
247    ///
248    /// # Errors
249    ///
250    /// Returns an error if:
251    /// - The index is out of bounds
252    /// - The field shapes don't match
253    pub fn set<T: Facet>(&mut self, index: usize, value: T) -> Result<(), FieldError> {
254        let field_shape = self
255            .def
256            .fields
257            .get(index)
258            .ok_or(FieldError::IndexOutOfBounds)?
259            .shape;
260        if !field_shape.is_type::<T>() {
261            return Err(FieldError::TypeMismatch);
262        }
263
264        unsafe {
265            let opaque = OpaqueConst::new(&value);
266            let result = self.unchecked_set(index, opaque);
267            if result.is_ok() {
268                core::mem::forget(value);
269            }
270            result
271        }
272    }
273
274    /// Sets a field's value by its name in a type-safe manner.
275    ///
276    /// This method takes ownership of the value and ensures proper memory management.
277    ///
278    /// # Errors
279    ///
280    /// Returns an error if:
281    /// - The field name doesn't exist
282    /// - The field shapes don't match
283    pub fn set_by_name<T: Facet>(&mut self, name: &str, value: T) -> Result<(), FieldError> {
284        let index = self
285            .def
286            .fields
287            .iter()
288            .position(|f| f.name == name)
289            .ok_or(FieldError::NoSuchStaticField)?;
290
291        self.set(index, value)
292    }
293
294    /// Marks a field as initialized.
295    ///
296    /// # Safety
297    ///
298    /// The caller must ensure that the field is initialized. Only call this after writing to
299    /// an address gotten through [`Self::field`] or [`Self::field_by_name`].
300    pub unsafe fn mark_initialized(&mut self, index: usize) {
301        self.iset.set(index);
302    }
303
304    /// Gets the struct definition
305    pub fn def(&self) -> StructDef {
306        self.def
307    }
308}
309
310impl Drop for PokeStruct<'_> {
311    fn drop(&mut self) {
312        self.def
313            .fields
314            .iter()
315            .enumerate()
316            .filter_map(|(i, field)| {
317                if self.iset.has(i) {
318                    Some((field, field.shape.vtable.drop_in_place?))
319                } else {
320                    None
321                }
322            })
323            .for_each(|(field, drop_fn)| unsafe {
324                drop_fn(self.data.field_init(field.offset));
325            });
326    }
327}