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}