1use core::ptr::NonNull;
2#[cfg(feature = "alloc")]
3extern crate alloc;
4#[cfg(feature = "alloc")]
5use alloc::boxed::Box;
6
7use facet_core::{EnumDef, EnumRepr, Facet, FieldError, Opaque, OpaqueUninit, Shape, VariantKind};
8
9use crate::Guard;
10
11use super::{ISet, PokeValueUninit};
12
13pub struct PokeEnumNoVariant<'mem> {
15 data: OpaqueUninit<'mem>,
16 shape: &'static Shape,
17 def: EnumDef,
18}
19
20impl<'mem> PokeEnumNoVariant<'mem> {
21 #[inline(always)]
23 pub fn into_value(self) -> PokeValueUninit<'mem> {
24 unsafe { PokeValueUninit::new(self.data, self.shape) }
25 }
26
27 #[inline(always)]
29 pub fn shape(&self) -> &'static Shape {
30 self.shape
31 }
32 pub(crate) unsafe fn new(
38 data: OpaqueUninit<'mem>,
39 shape: &'static Shape,
40 def: EnumDef,
41 ) -> Self {
42 Self { data, shape, def }
43 }
44
45 pub fn set_variant_by_name(self, variant_name: &str) -> Result<PokeEnum<'mem>, FieldError> {
52 let variant_index = self
53 .def
54 .variants
55 .iter()
56 .enumerate()
57 .find(|(_, v)| v.name == variant_name)
58 .map(|(i, _)| i)
59 .ok_or(FieldError::NoSuchStaticField)?;
60
61 self.set_variant_by_index(variant_index)
62 }
63
64 pub fn set_variant_by_index(self, variant_index: usize) -> Result<PokeEnum<'mem>, FieldError> {
71 if variant_index >= self.def.variants.len() {
72 return Err(FieldError::IndexOutOfBounds);
73 }
74
75 let variant = &self.def.variants[variant_index];
77
78 unsafe {
80 core::ptr::write_bytes(self.data.as_mut_bytes(), 0, self.shape.layout.size());
82
83 let discriminant_value = match &variant.discriminant {
86 Some(discriminant) => *discriminant,
88 None => variant_index as i64,
90 };
91
92 match self.def.repr {
94 EnumRepr::U8 => {
95 let tag_ptr = self.data.as_mut_bytes();
96 *tag_ptr = discriminant_value as u8;
97 }
98 EnumRepr::U16 => {
99 let tag_ptr = self.data.as_mut_bytes() as *mut u16;
100 *tag_ptr = discriminant_value as u16;
101 }
102 EnumRepr::U32 => {
103 let tag_ptr = self.data.as_mut_bytes() as *mut u32;
104 *tag_ptr = discriminant_value as u32;
105 }
106 EnumRepr::U64 => {
107 let tag_ptr = self.data.as_mut_bytes() as *mut u64;
108 *tag_ptr = discriminant_value as u64;
109 }
110 EnumRepr::USize => {
111 let tag_ptr = self.data.as_mut_bytes() as *mut usize;
112 *tag_ptr = discriminant_value as usize;
113 }
114 EnumRepr::I8 => {
115 let tag_ptr = self.data.as_mut_bytes() as *mut i8;
116 *tag_ptr = discriminant_value as i8;
117 }
118 EnumRepr::I16 => {
119 let tag_ptr = self.data.as_mut_bytes() as *mut i16;
120 *tag_ptr = discriminant_value as i16;
121 }
122 EnumRepr::I32 => {
123 let tag_ptr = self.data.as_mut_bytes() as *mut i32;
124 *tag_ptr = discriminant_value as i32;
125 }
126 EnumRepr::I64 => {
127 let tag_ptr = self.data.as_mut_bytes() as *mut i64;
128 *tag_ptr = discriminant_value;
129 }
130 EnumRepr::ISize => {
131 let tag_ptr = self.data.as_mut_bytes() as *mut isize;
132 *tag_ptr = discriminant_value as isize;
133 }
134 _ => {
135 panic!("Unsupported enum representation: {:?}", self.def.repr);
136 }
137 }
138 }
139
140 Ok(PokeEnum {
142 data: self.data,
143 iset: Default::default(),
144 shape: self.shape,
145 def: self.def,
146 selected_variant: variant_index,
147 })
148 }
149}
150
151pub struct PokeEnum<'mem> {
153 data: OpaqueUninit<'mem>,
159 iset: ISet,
160 shape: &'static Shape,
161 def: EnumDef,
162 selected_variant: usize,
163}
164
165impl<'mem> PokeEnum<'mem> {
166 #[inline(always)]
168 pub fn into_value(self) -> PokeValueUninit<'mem> {
169 unsafe { PokeValueUninit::new(self.data, self.shape) }
170 }
171
172 pub(crate) fn variant_data(&self) -> OpaqueUninit<'mem> {
173 let variant_offset = self.def.variants[self.selected_variant].offset;
174 unsafe { self.data.field_uninit(variant_offset) }
175 }
176
177 #[allow(dead_code)]
183 pub(crate) unsafe fn new(
184 data: OpaqueUninit<'mem>,
185 shape: &'static Shape,
186 def: EnumDef,
187 selected_variant: usize,
188 ) -> Self {
189 Self {
190 data,
191 iset: Default::default(),
192 shape,
193 def,
194 selected_variant,
195 }
196 }
197
198 #[inline(always)]
199 pub fn shape(&self) -> &'static Shape {
201 self.shape
202 }
203
204 pub fn selected_variant_index(&self) -> usize {
206 self.selected_variant
207 }
208
209 pub fn field_by_name(
217 &self,
218 name: &str,
219 ) -> Result<(usize, crate::PokeUninit<'mem>), FieldError> {
220 let variant = &self.def.variants[self.selected_variant];
221
222 match &variant.kind {
224 VariantKind::Unit => {
225 Err(FieldError::NoSuchStaticField)
227 }
228 VariantKind::Tuple { fields } => {
229 let (index, field) = fields
231 .iter()
232 .enumerate()
233 .find(|(_, f)| f.name == name)
234 .ok_or(FieldError::NoSuchStaticField)?;
235
236 let field_data = unsafe { self.variant_data().field_uninit(field.offset) };
238 let poke = unsafe { crate::PokeUninit::unchecked_new(field_data, field.shape) };
239 Ok((index, poke))
240 }
241 VariantKind::Struct { fields } => {
242 let (index, field) = fields
244 .iter()
245 .enumerate()
246 .find(|(_, f)| f.name == name)
247 .ok_or(FieldError::NoSuchStaticField)?;
248
249 let field_data = unsafe { self.variant_data().field_uninit(field.offset) };
251 let poke = unsafe { crate::PokeUninit::unchecked_new(field_data, field.shape) };
252 Ok((index, poke))
253 }
254 _ => {
255 panic!("Unsupported enum variant kind: {:?}", variant.kind);
256 }
257 }
258 }
259
260 pub fn tuple_field(&self, index: usize) -> Result<crate::PokeUninit<'mem>, FieldError> {
268 let variant = &self.def.variants[self.selected_variant];
269
270 match &variant.kind {
272 VariantKind::Tuple { fields } => {
273 if index >= fields.len() {
275 return Err(FieldError::IndexOutOfBounds);
276 }
277
278 let field = &fields[index];
280
281 let field_data = unsafe { self.variant_data().field_uninit(field.offset) };
283 let poke = unsafe { crate::PokeUninit::unchecked_new(field_data, field.shape) };
284 Ok(poke)
285 }
286 _ => {
287 Err(FieldError::NoSuchStaticField)
289 }
290 }
291 }
292
293 pub unsafe fn mark_initialized(&mut self, field_index: usize) {
300 self.iset.set(field_index);
301 }
302
303 pub fn assert_all_fields_initialized(&self) {
309 let variant = &self.def.variants[self.selected_variant];
310
311 match &variant.kind {
313 VariantKind::Unit => {
314 }
316 VariantKind::Tuple { fields } | VariantKind::Struct { fields } => {
317 for (field_index, field) in fields.iter().enumerate() {
319 if !self.iset.has(field_index) {
320 panic!(
321 "Field '{}' of variant '{}' was not initialized. Complete schema:\n{}",
322 field.name, variant.name, self.shape
323 );
324 }
325 }
326 }
327 _ => {
328 panic!("Unsupported enum variant kind: {:?}", variant.kind);
329 }
330 }
331 }
332
333 fn assert_matching_shape<T: Facet>(&self) {
334 if !self.shape.is_type::<T>() {
335 panic!(
336 "This is a partial \x1b[1;34m{}\x1b[0m, you can't build a \x1b[1;32m{}\x1b[0m out of it",
337 self.shape,
338 T::SHAPE,
339 );
340 }
341 }
342
343 pub fn build_in_place(self) -> Opaque<'mem> {
352 self.assert_all_fields_initialized();
354 let data = unsafe { self.data.assume_init() };
355 core::mem::forget(self);
357 data
358 }
359
360 pub fn build<T: Facet>(self, guard: Option<Guard>) -> T {
369 let mut guard = guard;
370 let this = self;
371 this.assert_all_fields_initialized();
374 this.assert_matching_shape::<T>();
375 if let Some(guard) = &guard {
376 guard.shape.assert_type::<T>();
377 }
378
379 let result = unsafe {
380 let ptr = this.data.as_mut_bytes() as *const T;
381 core::ptr::read(ptr)
382 };
383 guard.take(); core::mem::forget(this);
385 result
386 }
387
388 #[cfg(feature = "alloc")]
396 pub fn build_boxed<T: Facet>(self) -> Box<T> {
397 self.assert_all_fields_initialized();
398 self.assert_matching_shape::<T>();
399
400 let boxed = unsafe { Box::from_raw(self.data.as_mut_bytes() as *mut T) };
401 core::mem::forget(self);
402 boxed
403 }
404
405 pub unsafe fn move_into(self, target: NonNull<u8>) {
414 self.assert_all_fields_initialized();
415 unsafe {
416 core::ptr::copy_nonoverlapping(
417 self.data.as_mut_bytes(),
418 target.as_ptr(),
419 self.shape.layout.size(),
420 );
421 }
422 core::mem::forget(self);
423 }
424}
425
426impl Drop for PokeEnum<'_> {
427 fn drop(&mut self) {
428 let variant = &self.def.variants[self.selected_variant];
429
430 match &variant.kind {
432 VariantKind::Unit => {
433 }
435 VariantKind::Tuple { fields } | VariantKind::Struct { fields } => {
436 for (field_index, field) in fields.iter().enumerate() {
438 if self.iset.has(field_index) {
439 if let Some(drop_fn) = field.shape.vtable.drop_in_place {
440 unsafe {
441 drop_fn(self.variant_data().field_init(field.offset));
442 }
443 }
444 }
445 }
446 }
447 _ => {
448 panic!("Unsupported enum variant kind: {:?}", variant.kind);
449 }
450 }
451 }
452}
453
454#[derive(Debug, Copy, Clone, PartialEq, Eq)]
456#[non_exhaustive]
457pub enum VariantError {
458 IndexOutOfBounds,
460
461 NotAnEnum,
463
464 NoSuchVariant,
466}
467
468impl core::error::Error for VariantError {}
469
470impl core::fmt::Display for VariantError {
471 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
472 match self {
473 VariantError::IndexOutOfBounds => write!(f, "Variant index out of bounds"),
474 VariantError::NotAnEnum => write!(f, "Not an enum"),
475 VariantError::NoSuchVariant => write!(f, "No such variant"),
476 }
477 }
478}