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