1use core::ptr::NonNull;
2use facet_core::{EnumDef, EnumRepr, Facet, FieldError, Opaque, OpaqueUninit, Shape, VariantKind};
3
4use crate::Guard;
5
6use super::{ISet, PokeValueUninit};
7
8pub struct PokeEnumNoVariant<'mem> {
10 data: OpaqueUninit<'mem>,
11 shape: &'static Shape,
12 def: EnumDef,
13}
14
15impl<'mem> PokeEnumNoVariant<'mem> {
16 #[inline(always)]
18 pub fn into_value(self) -> PokeValueUninit<'mem> {
19 unsafe { PokeValueUninit::new(self.data, self.shape) }
20 }
21
22 #[inline(always)]
24 pub fn shape(&self) -> &'static Shape {
25 self.shape
26 }
27 pub(crate) unsafe fn new(
33 data: OpaqueUninit<'mem>,
34 shape: &'static Shape,
35 def: EnumDef,
36 ) -> Self {
37 Self { data, shape, def }
38 }
39
40 pub fn set_variant_by_name(self, variant_name: &str) -> Result<PokeEnum<'mem>, FieldError> {
47 let variant_index = self
48 .def
49 .variants
50 .iter()
51 .enumerate()
52 .find(|(_, v)| v.name == variant_name)
53 .map(|(i, _)| i)
54 .ok_or(FieldError::NoSuchStaticField)?;
55
56 self.set_variant_by_index(variant_index)
57 }
58
59 pub fn set_variant_by_index(self, variant_index: usize) -> Result<PokeEnum<'mem>, FieldError> {
66 if variant_index >= self.def.variants.len() {
67 return Err(FieldError::IndexOutOfBounds);
68 }
69
70 let variant = &self.def.variants[variant_index];
72
73 unsafe {
75 core::ptr::write_bytes(self.data.as_mut_bytes(), 0, self.shape.layout.size());
77
78 let discriminant_value = match &variant.discriminant {
81 Some(discriminant) => *discriminant,
83 None => variant_index as i64,
85 };
86
87 match self.def.repr {
89 EnumRepr::U8 => {
90 let tag_ptr = self.data.as_mut_bytes();
91 *tag_ptr = discriminant_value as u8;
92 }
93 EnumRepr::U16 => {
94 let tag_ptr = self.data.as_mut_bytes() as *mut u16;
95 *tag_ptr = discriminant_value as u16;
96 }
97 EnumRepr::U32 => {
98 let tag_ptr = self.data.as_mut_bytes() as *mut u32;
99 *tag_ptr = discriminant_value as u32;
100 }
101 EnumRepr::U64 => {
102 let tag_ptr = self.data.as_mut_bytes() as *mut u64;
103 *tag_ptr = discriminant_value as u64;
104 }
105 EnumRepr::USize => {
106 let tag_ptr = self.data.as_mut_bytes() as *mut usize;
107 *tag_ptr = discriminant_value as usize;
108 }
109 EnumRepr::I8 => {
110 let tag_ptr = self.data.as_mut_bytes() as *mut i8;
111 *tag_ptr = discriminant_value as i8;
112 }
113 EnumRepr::I16 => {
114 let tag_ptr = self.data.as_mut_bytes() as *mut i16;
115 *tag_ptr = discriminant_value as i16;
116 }
117 EnumRepr::I32 => {
118 let tag_ptr = self.data.as_mut_bytes() as *mut i32;
119 *tag_ptr = discriminant_value as i32;
120 }
121 EnumRepr::I64 => {
122 let tag_ptr = self.data.as_mut_bytes() as *mut i64;
123 *tag_ptr = discriminant_value;
124 }
125 EnumRepr::ISize => {
126 let tag_ptr = self.data.as_mut_bytes() as *mut isize;
127 *tag_ptr = discriminant_value as isize;
128 }
129 _ => {
130 panic!("Unsupported enum representation: {:?}", self.def.repr);
131 }
132 }
133 }
134
135 Ok(PokeEnum {
137 data: self.data,
138 iset: Default::default(),
139 shape: self.shape,
140 def: self.def,
141 selected_variant: variant_index,
142 })
143 }
144}
145
146pub struct PokeEnum<'mem> {
148 data: OpaqueUninit<'mem>,
149 iset: ISet,
150 shape: &'static Shape,
151 def: EnumDef,
152 selected_variant: usize,
153}
154
155impl<'mem> PokeEnum<'mem> {
156 #[inline(always)]
158 pub fn into_value(self) -> PokeValueUninit<'mem> {
159 unsafe { PokeValueUninit::new(self.data, self.shape) }
160 }
161
162 #[allow(dead_code)]
168 pub(crate) unsafe fn new(
169 data: OpaqueUninit<'mem>,
170 shape: &'static Shape,
171 def: EnumDef,
172 selected_variant: usize,
173 ) -> Self {
174 Self {
175 data,
176 iset: Default::default(),
177 shape,
178 def,
179 selected_variant,
180 }
181 }
182
183 #[inline(always)]
184 pub fn shape(&self) -> &'static Shape {
186 self.shape
187 }
188
189 pub fn selected_variant_index(&self) -> usize {
191 self.selected_variant
192 }
193
194 pub fn field_by_name(
202 &self,
203 name: &str,
204 ) -> Result<(usize, crate::PokeUninit<'mem>), FieldError> {
205 let variant = &self.def.variants[self.selected_variant];
206
207 match &variant.kind {
209 VariantKind::Unit => {
210 Err(FieldError::NoSuchStaticField)
212 }
213 VariantKind::Tuple { fields } => {
214 let (index, field) = fields
216 .iter()
217 .enumerate()
218 .find(|(_, f)| f.name == name)
219 .ok_or(FieldError::NoSuchStaticField)?;
220
221 let field_data = unsafe { self.data.field_uninit(field.offset) };
223 let poke = unsafe { crate::PokeUninit::unchecked_new(field_data, field.shape) };
224 Ok((index, poke))
225 }
226 VariantKind::Struct { fields } => {
227 let (index, field) = fields
229 .iter()
230 .enumerate()
231 .find(|(_, f)| f.name == name)
232 .ok_or(FieldError::NoSuchStaticField)?;
233
234 let field_data = unsafe { self.data.field_uninit(field.offset) };
236 let poke = unsafe { crate::PokeUninit::unchecked_new(field_data, field.shape) };
237 Ok((index, poke))
238 }
239 _ => {
240 panic!("Unsupported enum variant kind: {:?}", variant.kind);
241 }
242 }
243 }
244
245 pub fn tuple_field(&self, index: usize) -> Result<crate::PokeUninit<'mem>, FieldError> {
253 let variant = &self.def.variants[self.selected_variant];
254
255 match &variant.kind {
257 VariantKind::Tuple { fields } => {
258 if index >= fields.len() {
260 return Err(FieldError::IndexOutOfBounds);
261 }
262
263 let field = &fields[index];
265
266 let field_data = unsafe { self.data.field_uninit(field.offset) };
268 let poke = unsafe { crate::PokeUninit::unchecked_new(field_data, field.shape) };
269 Ok(poke)
270 }
271 _ => {
272 Err(FieldError::NoSuchStaticField)
274 }
275 }
276 }
277
278 pub unsafe fn mark_initialized(&mut self, field_index: usize) {
285 self.iset.set(field_index);
286 }
287
288 pub fn assert_all_fields_initialized(&self) {
294 let variant = &self.def.variants[self.selected_variant];
295
296 match &variant.kind {
298 VariantKind::Unit => {
299 }
301 VariantKind::Tuple { fields } | VariantKind::Struct { fields } => {
302 for (field_index, field) in fields.iter().enumerate() {
304 if !self.iset.has(field_index) {
305 panic!(
306 "Field '{}' of variant '{}' was not initialized. Complete schema:\n{}",
307 field.name, variant.name, self.shape
308 );
309 }
310 }
311 }
312 _ => {
313 panic!("Unsupported enum variant kind: {:?}", variant.kind);
314 }
315 }
316 }
317
318 fn assert_matching_shape<T: Facet>(&self) {
319 if !self.shape.is_type::<T>() {
320 panic!(
321 "This is a partial \x1b[1;34m{}\x1b[0m, you can't build a \x1b[1;32m{}\x1b[0m out of it",
322 self.shape,
323 T::SHAPE,
324 );
325 }
326 }
327
328 pub fn build_in_place(self) -> Opaque<'mem> {
337 self.assert_all_fields_initialized();
339 let data = unsafe { self.data.assume_init() };
340 core::mem::forget(self);
342 data
343 }
344
345 pub fn build<T: Facet>(self, guard: Option<Guard>) -> T {
354 let mut guard = guard;
355 let this = self;
356 this.assert_all_fields_initialized();
359 this.assert_matching_shape::<T>();
360 if let Some(guard) = &guard {
361 guard.shape.assert_type::<T>();
362 }
363
364 let result = unsafe {
365 let ptr = this.data.as_mut_bytes() as *const T;
366 core::ptr::read(ptr)
367 };
368 guard.take(); core::mem::forget(this);
370 result
371 }
372
373 pub fn build_boxed<T: Facet>(self) -> alloc::boxed::Box<T> {
381 self.assert_all_fields_initialized();
382 self.assert_matching_shape::<T>();
383
384 let boxed = unsafe { alloc::boxed::Box::from_raw(self.data.as_mut_bytes() as *mut T) };
385 core::mem::forget(self);
386 boxed
387 }
388
389 pub unsafe fn move_into(self, target: NonNull<u8>) {
398 self.assert_all_fields_initialized();
399 unsafe {
400 core::ptr::copy_nonoverlapping(
401 self.data.as_mut_bytes(),
402 target.as_ptr(),
403 self.shape.layout.size(),
404 );
405 }
406 core::mem::forget(self);
407 }
408}
409
410impl Drop for PokeEnum<'_> {
411 fn drop(&mut self) {
412 let variant = &self.def.variants[self.selected_variant];
413
414 match &variant.kind {
416 VariantKind::Unit => {
417 }
419 VariantKind::Tuple { fields } | VariantKind::Struct { fields } => {
420 for (field_index, field) in fields.iter().enumerate() {
422 if self.iset.has(field_index) {
423 if let Some(drop_fn) = field.shape.vtable.drop_in_place {
424 unsafe {
425 drop_fn(self.data.field_init(field.offset));
426 }
427 }
428 }
429 }
430 }
431 _ => {
432 panic!("Unsupported enum variant kind: {:?}", variant.kind);
433 }
434 }
435 }
436}
437
438#[derive(Debug, Copy, Clone, PartialEq, Eq)]
440#[non_exhaustive]
441pub enum VariantError {
442 IndexOutOfBounds,
444
445 NotAnEnum,
447
448 NoSuchVariant,
450}
451
452impl core::error::Error for VariantError {}
453
454impl core::fmt::Display for VariantError {
455 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
456 match self {
457 VariantError::IndexOutOfBounds => write!(f, "Variant index out of bounds"),
458 VariantError::NotAnEnum => write!(f, "Not an enum"),
459 VariantError::NoSuchVariant => write!(f, "No such variant"),
460 }
461 }
462}