1use core::ptr::NonNull;
2use facet_trait::{FieldError, Opaque, OpaqueConst, OpaqueUninit, Shape, ShapeExt as _, StructDef};
3
4use super::{Guard, ISet, PokeValue};
5
6pub 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 pub fn into_value(self) -> PokeValue<'mem> {
18 unsafe { PokeValue::new(self.data, self.shape) }
19 }
20
21 #[inline(always)]
23 pub fn shape(&self) -> &'static Shape {
24 self.shape
25 }
26 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 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 pub fn build_in_place(self) -> Opaque<'mem> {
62 self.assert_all_fields_initialized();
64
65 let data = unsafe { self.data.assume_init() };
66
67 core::mem::forget(self);
69
70 data
71 }
72
73 pub fn build<T: crate::Facet>(self, guard: Option<Guard>) -> T {
82 let mut guard = guard;
83 let this = self;
84 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_ptr() as *const T;
94 core::ptr::read(ptr)
95 };
96 guard.take(); core::mem::forget(this);
98 result
99 }
100
101 pub fn build_boxed<T: crate::Facet>(self) -> Box<T> {
109 self.assert_all_fields_initialized();
110 self.shape.assert_type::<T>();
111
112 let boxed = unsafe { Box::from_raw(self.data.as_mut_ptr() as *mut T) };
113 core::mem::forget(self);
114 boxed
115 }
116
117 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_ptr(),
134 target.as_ptr(),
135 self.shape.layout.size(),
136 );
137 }
138 core::mem::forget(self);
139 }
140
141 pub fn field_by_name(&self, name: &str) -> Result<(usize, crate::Poke<'mem>), FieldError> {
143 let index = self
144 .def
145 .fields
146 .iter()
147 .position(|f| f.name == name)
148 .ok_or(FieldError::NoSuchStaticField)?;
149 Ok((index, self.field(index)?))
150 }
151
152 pub fn field(&self, index: usize) -> Result<crate::Poke<'mem>, FieldError> {
160 if index >= self.def.fields.len() {
161 return Err(FieldError::IndexOutOfBounds);
162 }
163
164 let field = &self.def.fields[index];
165
166 let field_addr = unsafe { self.data.field_uninit(field.offset) };
168 let field_shape = field.shape;
169
170 let poke = unsafe { crate::Poke::unchecked_new(field_addr, field_shape) };
171 Ok(poke)
172 }
173
174 pub fn set(&mut self, index: usize, value: OpaqueConst) -> Result<(), FieldError> {
182 if index >= self.def.fields.len() {
183 return Err(FieldError::IndexOutOfBounds);
184 }
185 let field = &self.def.fields[index];
186 let field_shape = field.shape;
187
188 unsafe {
189 core::ptr::copy_nonoverlapping(
190 value.as_ptr(),
191 self.data.field_uninit(field.offset).as_mut_ptr(),
192 field_shape.layout.size(),
193 );
194 self.iset.set(index);
195 }
196
197 Ok(())
198 }
199
200 pub fn set_by_name(&mut self, name: &str, value: OpaqueConst) -> Result<(), FieldError> {
208 let index = self
209 .def
210 .fields
211 .iter()
212 .position(|f| f.name == name)
213 .ok_or(FieldError::NoSuchStaticField)?;
214 self.set(index, value)
215 }
216
217 pub unsafe fn mark_initialized(&mut self, index: usize) {
224 self.iset.set(index);
225 }
226
227 pub fn def(&self) -> StructDef {
229 self.def
230 }
231}
232
233impl Drop for PokeStruct<'_> {
234 fn drop(&mut self) {
235 self.def
236 .fields
237 .iter()
238 .enumerate()
239 .filter_map(|(i, field)| {
240 if self.iset.has(i) {
241 Some((field, field.shape.vtable.drop_in_place?))
242 } else {
243 None
244 }
245 })
246 .for_each(|(field, drop_fn)| unsafe {
247 drop_fn(self.data.field_init(field.offset));
248 });
249 }
250}