intuicio_core/types/
enum_type.rs

1#![allow(unpredictable_function_pointer_comparisons)]
2
3//! NOTE: For now only acceptable enums are ones with `repr(u8)`,
4//! because those have stable discriminant offset and size.
5use crate::{
6    Visibility,
7    meta::Meta,
8    object::RuntimeObject,
9    types::{EnumVariantQuery, MetaQuery, StructFieldQuery, Type, struct_type::StructField},
10};
11use intuicio_data::{Finalize, Initialize, is_copy, is_send, is_sync, type_hash::TypeHash};
12use rustc_hash::FxHasher;
13use std::{
14    alloc::Layout,
15    borrow::Cow,
16    hash::{Hash, Hasher},
17};
18
19pub struct RuntimeEnumBuilder {
20    meta: Option<Meta>,
21    name: String,
22    module_name: Option<String>,
23    visibility: Visibility,
24    type_hash: TypeHash,
25    type_name: String,
26    variants: Vec<EnumVariant>,
27    defaut_variant: Option<u8>,
28    layout: Layout,
29    initializer: unsafe fn(*mut ()),
30    finalizer: unsafe fn(*mut ()),
31}
32
33impl RuntimeEnumBuilder {
34    pub fn new(name: impl ToString) -> Self {
35        Self {
36            meta: None,
37            name: name.to_string(),
38            module_name: None,
39            visibility: Visibility::default(),
40            type_hash: TypeHash::of::<RuntimeObject>(),
41            type_name: std::any::type_name::<RuntimeObject>().to_owned(),
42            variants: vec![],
43            defaut_variant: None,
44            layout: Layout::from_size_align(0, 1).unwrap(),
45            initializer: RuntimeObject::initialize_raw,
46            finalizer: RuntimeObject::finalize_raw,
47        }
48    }
49
50    pub fn meta(mut self, meta: Meta) -> Self {
51        self.meta = Some(meta);
52        self
53    }
54
55    pub fn maybe_meta(mut self, meta: Option<Meta>) -> Self {
56        self.meta = meta;
57        self
58    }
59
60    pub fn module_name(mut self, module_name: impl ToString) -> Self {
61        self.module_name = Some(module_name.to_string());
62        self
63    }
64
65    pub fn visibility(mut self, visibility: Visibility) -> Self {
66        self.visibility = visibility;
67        self
68    }
69
70    pub fn variant(mut self, mut variant: EnumVariant) -> Self {
71        variant.discriminant = self
72            .variants
73            .last()
74            .map(|variant| variant.discriminant + 1)
75            .unwrap_or(0);
76        self.variants.push(variant);
77        self
78    }
79
80    pub fn variant_with_discriminant(mut self, mut variant: EnumVariant, discriminant: u8) -> Self {
81        variant.discriminant = discriminant;
82        self.variants.push(variant);
83        self
84    }
85
86    pub fn set_default_variant(mut self, discriminant: u8) -> Self {
87        self.defaut_variant = Some(discriminant);
88        self
89    }
90
91    pub fn build(mut self) -> Enum {
92        self.variants.sort_by_key(|a| a.discriminant);
93        self.layout = Layout::new::<u8>();
94        for variant in &mut self.variants {
95            let mut layout = Layout::new::<u8>();
96            for field in &mut variant.fields {
97                let (new_layout, offset) = layout.extend(*field.type_handle.layout()).unwrap();
98                layout = new_layout;
99                field.offset = offset;
100            }
101            self.layout = Layout::from_size_align(
102                self.layout.size().max(layout.size()),
103                self.layout.align().max(layout.align()),
104            )
105            .unwrap();
106        }
107        let mut is_send = true;
108        let mut is_sync = true;
109        let mut is_copy = true;
110        for variant in &mut self.variants {
111            variant.fields.sort_by_key(|a| a.offset);
112            is_send = is_send
113                && variant
114                    .fields
115                    .iter()
116                    .all(|field| field.type_handle.is_send());
117            is_sync = is_sync
118                && variant
119                    .fields
120                    .iter()
121                    .all(|field| field.type_handle.is_sync());
122            is_copy = is_copy
123                && variant
124                    .fields
125                    .iter()
126                    .all(|field| field.type_handle.is_copy());
127        }
128        Enum {
129            meta: self.meta,
130            name: self.name,
131            module_name: self.module_name,
132            visibility: self.visibility,
133            type_hash: self.type_hash,
134            type_name: self.type_name,
135            variants: self.variants,
136            default_variant: self.defaut_variant,
137            layout: self.layout.pad_to_align(),
138            initializer: Some(self.initializer),
139            finalizer: self.finalizer,
140            is_send,
141            is_sync,
142            is_copy,
143        }
144    }
145}
146
147impl From<Enum> for RuntimeEnumBuilder {
148    fn from(value: Enum) -> Self {
149        Self {
150            meta: value.meta,
151            name: value.name,
152            module_name: value.module_name,
153            visibility: value.visibility,
154            type_hash: value.type_hash,
155            type_name: value.type_name,
156            variants: value.variants,
157            defaut_variant: value.default_variant,
158            layout: value.layout,
159            initializer: value.initializer.unwrap_or(RuntimeObject::initialize_raw),
160            finalizer: value.finalizer,
161        }
162    }
163}
164
165#[derive(Debug)]
166pub struct NativeEnumBuilder {
167    meta: Option<Meta>,
168    name: String,
169    module_name: Option<String>,
170    visibility: Visibility,
171    type_hash: TypeHash,
172    type_name: String,
173    variants: Vec<EnumVariant>,
174    defaut_variant: Option<u8>,
175    layout: Layout,
176    initializer: Option<unsafe fn(*mut ())>,
177    finalizer: unsafe fn(*mut ()),
178    is_send: bool,
179    is_sync: bool,
180    is_copy: bool,
181}
182
183impl NativeEnumBuilder {
184    pub fn new<T: Initialize + Finalize + 'static>() -> Self {
185        Self {
186            meta: None,
187            name: std::any::type_name::<T>().to_owned(),
188            module_name: None,
189            visibility: Visibility::default(),
190            type_hash: TypeHash::of::<T>(),
191            type_name: std::any::type_name::<T>().to_owned(),
192            variants: vec![],
193            defaut_variant: None,
194            layout: Layout::new::<T>().pad_to_align(),
195            initializer: Some(T::initialize_raw),
196            finalizer: T::finalize_raw,
197            is_send: is_send::<T>(),
198            is_sync: is_sync::<T>(),
199            is_copy: is_copy::<T>(),
200        }
201    }
202
203    pub fn new_named<T: Initialize + Finalize + 'static>(name: impl ToString) -> Self {
204        Self {
205            meta: None,
206            name: name.to_string(),
207            module_name: None,
208            visibility: Visibility::default(),
209            type_hash: TypeHash::of::<T>(),
210            type_name: std::any::type_name::<T>().to_owned(),
211            variants: vec![],
212            defaut_variant: None,
213            layout: Layout::new::<T>().pad_to_align(),
214            initializer: Some(T::initialize_raw),
215            finalizer: T::finalize_raw,
216            is_send: is_send::<T>(),
217            is_sync: is_sync::<T>(),
218            is_copy: is_copy::<T>(),
219        }
220    }
221
222    pub fn new_uninitialized<T: Finalize + 'static>() -> Self {
223        Self {
224            meta: None,
225            name: std::any::type_name::<T>().to_owned(),
226            module_name: None,
227            visibility: Visibility::default(),
228            type_hash: TypeHash::of::<T>(),
229            type_name: std::any::type_name::<T>().to_owned(),
230            variants: vec![],
231            defaut_variant: None,
232            layout: Layout::new::<T>().pad_to_align(),
233            initializer: None,
234            finalizer: T::finalize_raw,
235            is_send: is_send::<T>(),
236            is_sync: is_sync::<T>(),
237            is_copy: is_copy::<T>(),
238        }
239    }
240
241    pub fn new_named_uninitialized<T: Finalize + 'static>(name: impl ToString) -> Self {
242        Self {
243            meta: None,
244            name: name.to_string(),
245            module_name: None,
246            visibility: Visibility::default(),
247            type_hash: TypeHash::of::<T>(),
248            type_name: std::any::type_name::<T>().to_owned(),
249            variants: vec![],
250            defaut_variant: None,
251            layout: Layout::new::<T>().pad_to_align(),
252            initializer: None,
253            finalizer: T::finalize_raw,
254            is_send: is_send::<T>(),
255            is_sync: is_sync::<T>(),
256            is_copy: is_copy::<T>(),
257        }
258    }
259
260    pub fn meta(mut self, meta: Meta) -> Self {
261        self.meta = Some(meta);
262        self
263    }
264
265    pub fn maybe_meta(mut self, meta: Option<Meta>) -> Self {
266        self.meta = meta;
267        self
268    }
269
270    pub fn module_name(mut self, module_name: impl ToString) -> Self {
271        self.module_name = Some(module_name.to_string());
272        self
273    }
274
275    pub fn visibility(mut self, visibility: Visibility) -> Self {
276        self.visibility = visibility;
277        self
278    }
279
280    pub fn variant(mut self, mut variant: EnumVariant, discriminant: u8) -> Self {
281        self.is_send = self.is_send && variant.is_send();
282        self.is_sync = self.is_sync && variant.is_sync();
283        self.is_copy = self.is_copy && variant.is_copy();
284        variant.discriminant = discriminant;
285        self.variants.push(variant);
286        self
287    }
288
289    pub fn set_default_variant(mut self, discriminant: u8) -> Self {
290        self.defaut_variant = Some(discriminant);
291        self
292    }
293
294    /// # Safety
295    pub unsafe fn override_send(mut self, mode: bool) -> Self {
296        self.is_send = mode;
297        self
298    }
299
300    /// # Safety
301    pub unsafe fn override_sync(mut self, mode: bool) -> Self {
302        self.is_sync = mode;
303        self
304    }
305
306    /// # Safety
307    pub unsafe fn override_copy(mut self, mode: bool) -> Self {
308        self.is_copy = mode;
309        self
310    }
311
312    pub fn build(mut self) -> Enum {
313        self.variants.sort_by_key(|a| a.discriminant);
314        for variant in &mut self.variants {
315            variant.fields.sort_by_key(|a| a.offset);
316        }
317        Enum {
318            meta: self.meta,
319            name: self.name,
320            module_name: self.module_name,
321            visibility: self.visibility,
322            type_hash: self.type_hash,
323            type_name: self.type_name,
324            variants: self.variants,
325            default_variant: self.defaut_variant,
326            layout: self.layout,
327            initializer: self.initializer,
328            finalizer: self.finalizer,
329            is_send: self.is_send,
330            is_sync: self.is_sync,
331            is_copy: self.is_copy,
332        }
333    }
334}
335
336impl From<Enum> for NativeEnumBuilder {
337    fn from(value: Enum) -> Self {
338        Self {
339            meta: value.meta,
340            name: value.name,
341            module_name: value.module_name,
342            visibility: value.visibility,
343            type_hash: value.type_hash,
344            type_name: value.type_name,
345            variants: value.variants,
346            defaut_variant: value.default_variant,
347            layout: value.layout,
348            initializer: value.initializer,
349            finalizer: value.finalizer,
350            is_send: value.is_send,
351            is_sync: value.is_sync,
352            is_copy: value.is_copy,
353        }
354    }
355}
356
357#[derive(Debug, PartialEq)]
358pub struct EnumVariant {
359    pub meta: Option<Meta>,
360    pub name: String,
361    pub fields: Vec<StructField>,
362    discriminant: u8,
363}
364
365impl EnumVariant {
366    pub fn new(name: impl ToString) -> Self {
367        Self {
368            meta: None,
369            name: name.to_string(),
370            fields: vec![],
371            discriminant: 0,
372        }
373    }
374
375    pub fn with_meta(mut self, meta: Meta) -> Self {
376        self.meta = Some(meta);
377        self
378    }
379
380    pub fn with_field(mut self, field: StructField) -> Self {
381        self.fields.push(field);
382        self
383    }
384
385    pub fn with_field_with_offset(mut self, mut field: StructField, offset: usize) -> Self {
386        field.offset = offset;
387        self.fields.push(field);
388        self
389    }
390
391    pub fn discriminant(&self) -> u8 {
392        self.discriminant
393    }
394
395    pub fn is_send(&self) -> bool {
396        self.fields.iter().all(|f| f.type_handle.is_send())
397    }
398
399    pub fn is_sync(&self) -> bool {
400        self.fields.iter().all(|f| f.type_handle.is_sync())
401    }
402
403    pub fn is_copy(&self) -> bool {
404        self.fields.iter().all(|f| f.type_handle.is_copy())
405    }
406
407    pub fn find_fields<'a>(
408        &'a self,
409        query: StructFieldQuery<'a>,
410    ) -> impl Iterator<Item = &'a StructField> + 'a {
411        self.fields
412            .iter()
413            .filter(move |field| query.is_valid(field))
414    }
415
416    pub fn find_field<'a>(&'a self, query: StructFieldQuery<'a>) -> Option<&'a StructField> {
417        self.find_fields(query).next()
418    }
419}
420
421#[derive(Debug)]
422pub struct Enum {
423    pub meta: Option<Meta>,
424    pub name: String,
425    pub module_name: Option<String>,
426    pub visibility: Visibility,
427    type_hash: TypeHash,
428    type_name: String,
429    variants: Vec<EnumVariant>,
430    default_variant: Option<u8>,
431    layout: Layout,
432    initializer: Option<unsafe fn(*mut ())>,
433    finalizer: unsafe fn(*mut ()),
434    is_send: bool,
435    is_sync: bool,
436    is_copy: bool,
437}
438
439impl Enum {
440    pub fn is_runtime(&self) -> bool {
441        self.type_hash == TypeHash::of::<RuntimeObject>()
442    }
443
444    pub fn is_native(&self) -> bool {
445        !self.is_runtime()
446    }
447
448    pub fn is_send(&self) -> bool {
449        self.is_send
450    }
451
452    pub fn is_sync(&self) -> bool {
453        self.is_sync
454    }
455
456    pub fn is_copy(&self) -> bool {
457        self.is_copy
458    }
459
460    pub fn can_initialize(&self) -> bool {
461        self.initializer.is_some()
462    }
463
464    pub fn type_hash(&self) -> TypeHash {
465        self.type_hash
466    }
467
468    pub fn type_name(&self) -> &str {
469        &self.type_name
470    }
471
472    pub fn layout(&self) -> &Layout {
473        &self.layout
474    }
475
476    pub fn variants(&self) -> &[EnumVariant] {
477        &self.variants
478    }
479
480    pub fn default_variant_discriminant(&self) -> Option<u8> {
481        self.default_variant
482    }
483
484    pub fn default_variant(&self) -> Option<&EnumVariant> {
485        let discriminant = self.default_variant?;
486        self.variants
487            .iter()
488            .find(|variant| variant.discriminant == discriminant)
489    }
490
491    pub fn is_compatible(&self, other: &Self) -> bool {
492        self.layout == other.layout && self.variants == other.variants
493    }
494
495    pub fn find_variants<'a>(
496        &'a self,
497        query: EnumVariantQuery<'a>,
498    ) -> impl Iterator<Item = &'a EnumVariant> + 'a {
499        self.variants
500            .iter()
501            .filter(move |variant| query.is_valid(variant))
502    }
503
504    pub fn find_variant<'a>(&'a self, query: EnumVariantQuery<'a>) -> Option<&'a EnumVariant> {
505        self.find_variants(query).next()
506    }
507
508    /// # Safety
509    pub unsafe fn find_variant_by_value<T: 'static>(&self, value: &T) -> Option<&EnumVariant> {
510        if TypeHash::of::<T>() == self.type_hash {
511            let discriminant = unsafe { (value as *const T as *const u8).read() };
512            self.variants
513                .iter()
514                .find(|variant| variant.discriminant == discriminant)
515        } else {
516            None
517        }
518    }
519
520    pub fn find_variant_by_discriminant(&self, discriminant: u8) -> Option<&EnumVariant> {
521        self.variants
522            .iter()
523            .find(|variant| variant.discriminant == discriminant)
524    }
525
526    /// # Safety
527    pub unsafe fn try_copy(&self, from: *const u8, to: *mut u8) -> bool {
528        if !self.is_send {
529            return false;
530        }
531        let size = self.layout.size();
532        if from < unsafe { to.add(size) } && unsafe { from.add(size) } > to {
533            return false;
534        }
535        unsafe { to.copy_from_nonoverlapping(from, size) };
536        true
537    }
538
539    /// # Safety
540    pub unsafe fn initialize(&self, pointer: *mut ()) -> bool {
541        if let Some(initializer) = self.initializer {
542            unsafe { (initializer)(pointer) };
543            true
544        } else {
545            false
546        }
547    }
548
549    /// # Safety
550    pub unsafe fn finalize(&self, pointer: *mut ()) {
551        unsafe { (self.finalizer)(pointer) };
552    }
553
554    /// # Safety
555    pub unsafe fn initializer(&self) -> Option<unsafe fn(*mut ())> {
556        self.initializer
557    }
558
559    /// # Safety
560    pub unsafe fn finalizer(&self) -> unsafe fn(*mut ()) {
561        self.finalizer
562    }
563
564    pub fn into_type(self) -> Type {
565        self.into()
566    }
567}
568
569impl PartialEq for Enum {
570    fn eq(&self, other: &Self) -> bool {
571        self.name == other.name
572            && self.type_hash == other.type_hash
573            && self.layout == other.layout
574            && self.variants == other.variants
575    }
576}
577
578#[derive(Debug, Default, Clone, PartialEq, Hash)]
579pub struct EnumQuery<'a> {
580    pub name: Option<Cow<'a, str>>,
581    pub module_name: Option<Cow<'a, str>>,
582    pub type_hash: Option<TypeHash>,
583    pub type_name: Option<Cow<'a, str>>,
584    pub visibility: Option<Visibility>,
585    pub variants: Cow<'a, [EnumVariantQuery<'a>]>,
586    pub meta: Option<MetaQuery>,
587}
588
589impl<'a> EnumQuery<'a> {
590    pub fn of_type_name<T: 'static>() -> Self {
591        Self {
592            type_name: Some(std::any::type_name::<T>().into()),
593            ..Default::default()
594        }
595    }
596
597    pub fn of<T: 'static>() -> Self {
598        Self {
599            type_hash: Some(TypeHash::of::<T>()),
600            ..Default::default()
601        }
602    }
603
604    pub fn of_named<T: 'static>(name: &'a str) -> Self {
605        Self {
606            name: Some(name.into()),
607            type_hash: Some(TypeHash::of::<T>()),
608            ..Default::default()
609        }
610    }
611
612    pub fn is_valid(&self, enum_type: &Enum) -> bool {
613        self.name
614            .as_ref()
615            .map(|name| name.as_ref() == enum_type.name)
616            .unwrap_or(true)
617            && self
618                .module_name
619                .as_ref()
620                .map(|name| {
621                    enum_type
622                        .module_name
623                        .as_ref()
624                        .map(|module_name| name.as_ref() == module_name)
625                        .unwrap_or(false)
626                })
627                .unwrap_or(true)
628            && self
629                .type_hash
630                .map(|type_hash| enum_type.type_hash == type_hash)
631                .unwrap_or(true)
632            && self
633                .type_name
634                .as_ref()
635                .map(|type_name| enum_type.type_name == type_name.as_ref())
636                .unwrap_or(true)
637            && self
638                .visibility
639                .map(|visibility| enum_type.visibility.is_visible(visibility))
640                .unwrap_or(true)
641            && self
642                .variants
643                .iter()
644                .zip(enum_type.variants.iter())
645                .all(|(query, field)| query.is_valid(field))
646            && self
647                .meta
648                .as_ref()
649                .map(|query| enum_type.meta.as_ref().map(query).unwrap_or(false))
650                .unwrap_or(true)
651    }
652
653    pub fn as_hash(&self) -> u64 {
654        let mut hasher = FxHasher::default();
655        self.hash(&mut hasher);
656        hasher.finish()
657    }
658
659    pub fn to_static(&self) -> EnumQuery<'static> {
660        EnumQuery {
661            name: self
662                .name
663                .as_ref()
664                .map(|name| name.as_ref().to_owned().into()),
665            module_name: self
666                .module_name
667                .as_ref()
668                .map(|name| name.as_ref().to_owned().into()),
669            type_hash: self.type_hash,
670            type_name: self
671                .type_name
672                .as_ref()
673                .map(|name| name.as_ref().to_owned().into()),
674            visibility: self.visibility,
675            variants: self
676                .variants
677                .as_ref()
678                .iter()
679                .map(|query| query.to_static())
680                .collect(),
681            meta: self.meta,
682        }
683    }
684}
685
686#[macro_export]
687macro_rules! define_native_enum {
688    (
689        $registry:expr
690        =>
691        $(mod $module_name:ident)?
692        enum $($name:ident)? ($type:tt) {
693            $( $variant:tt )*
694        }
695        [uninitialized]
696        $( [override_send = $override_send:literal] )?
697        $( [override_sync = $override_sync:literal] )?
698        $( [override_copy = $override_copy:literal] )?
699    ) => {{
700        #[allow(unused_mut)]
701        let mut override_send = Option::<bool>::None;
702        $(
703            override_send = Some($override_send as bool);
704        )?
705        #[allow(unused_mut)]
706        let mut override_sync = Option::<bool>::None;
707        $(
708            override_sync = Some($override_sync as bool);
709        )?
710        #[allow(unused_mut)]
711        let mut override_copy = Option::<bool>::None;
712        $(
713            override_copy = Some($override_copy as bool);
714        )?
715        #[allow(unused_mut)]
716        let mut name = std::any::type_name::<$type>().to_owned();
717        $(
718            name = stringify!($name).to_owned();
719        )?
720        #[allow(unused_mut)]
721        let mut result = $crate::types::enum_type::NativeEnumBuilder::new_named_uninitialized::<$type>(name);
722        $(
723            result = result.module_name(stringify!($module_name).to_owned());
724        )?
725        $( $crate::define_native_enum! { @variant $registry => result => $type => $variant } )*
726        if let Some(mode) = override_send {
727            result = unsafe { result.override_send(mode) };
728        }
729        if let Some(mode) = override_sync {
730            result = unsafe { result.override_sync(mode) };
731        }
732        if let Some(mode) = override_copy {
733            result = unsafe { result.override_copy(mode) };
734        }
735        result.build()
736    }};
737    (
738        $registry:expr
739        =>
740        $(mod $module_name:ident)?
741        enum $($name:ident)? ($type:tt) {
742            $( $variant:tt )*
743        }
744        $( [override_send = $override_send:literal] )?
745        $( [override_sync = $override_sync:literal] )?
746        $( [override_copy = $override_copy:literal] )?
747    ) => {{
748        #[allow(unused_mut)]
749        let mut override_send = Option::<bool>::None;
750        $(
751            override_send = Some($override_send as bool);
752        )?
753        #[allow(unused_mut)]
754        let mut override_sync = Option::<bool>::None;
755        $(
756            override_sync = Some($override_sync as bool);
757        )?
758        #[allow(unused_mut)]
759        let mut override_copy = Option::<bool>::None;
760        $(
761            override_copy = Some($override_copy as bool);
762        )?
763        #[allow(unused_mut)]
764        let mut name = std::any::type_name::<$type>().to_owned();
765        $(
766            name = stringify!($name).to_owned();
767        )?
768        #[allow(unused_mut)]
769        let mut result = $crate::types::enum_type::NativeEnumBuilder::new_named::<$type>(name);
770        $(
771            result = result.module_name(stringify!($module_name).to_owned());
772        )?
773        $( $crate::define_native_enum! { @variant $registry => result => $type => $variant } )*
774        if let Some(mode) = override_send {
775            result = unsafe { result.override_send(mode) };
776        }
777        if let Some(mode) = override_sync {
778            result = unsafe { result.override_sync(mode) };
779        }
780        if let Some(mode) = override_copy {
781            result = unsafe { result.override_copy(mode) };
782        }
783        result.build()
784    }};
785    (@fields_tuple $registry:expr => $variant:expr => $type:tt => $name:ident => {
786        $current_field_name:ident : $current_field_type:ty $( , $rest_field_name:ident : $rest_field_type:ty )*
787    } => { $($field_name:ident),* } => $discriminant:literal) => {
788        $variant = $variant.with_field_with_offset(
789            $crate::types::struct_type::StructField::new(
790                stringify!($current_field_name),
791                $registry
792                    .find_type($crate::types::TypeQuery::of::<$current_field_type>())
793                    .unwrap(),
794            ),
795            $crate::__internal__offset_of_enum__!(
796                $type :: $name [$($field_name),*] => $current_field_name => $discriminant
797            ),
798        );
799        $crate::define_native_enum! { @fields_tuple $registry => $variant => $type => $name => {
800            $( $rest_field_name : $rest_field_type ),*
801        } => { $( $field_name ),* } => $discriminant }
802    };
803    (@fields_tuple $registry:expr => $variant:expr => $type:tt => $name:ident => {} => { $($field_name:ident),* } => $discriminant:literal) => {};
804    (@variant $registry:expr => $result:expr => $type:tt => {
805        $name:ident ( $( $field_name:ident : $field_type:ty ),* ) = $discriminant:literal
806    }) => {
807        $result = {
808            #[allow(unused_mut)]
809            let mut variant = $crate::types::enum_type::EnumVariant::new(stringify!($name));
810            $crate::define_native_enum! { @fields_tuple $registry => variant => $type => $name => {
811                $( $field_name : $field_type ),*
812            } => { $( $field_name ),* } => $discriminant }
813            $result.variant(variant, $discriminant)
814        };
815    };
816    (@variant $registry:expr => $result:expr => $type:tt => {
817        $name:ident { $( $field_name:ident : $field_type:ty ),* } = $discriminant:literal
818    }) => {
819        $result = {
820            #[allow(unused_mut)]
821            let mut variant = $crate::types::enum_type::EnumVariant::new(stringify!($name));
822            $(
823                variant = variant.with_field_with_offset(
824                    $crate::types::struct_type::StructField::new(
825                        stringify!($field_name),
826                        $registry
827                            .find_type($crate::types::TypeQuery::of::<$field_type>())
828                            .unwrap(),
829                    ),
830                    $crate::__internal__offset_of_enum__!(
831                        $type :: $name { $field_name } => $discriminant
832                    ),
833                );
834            )*
835            $result.variant(variant, $discriminant)
836        };
837    };
838    (@variant $registry:expr => $result:expr => $type:tt => {
839        $name:ident = $discriminant:literal
840    }) => {
841        $result = {
842            let variant = $crate::types::enum_type::EnumVariant::new(stringify!($name));
843            $result.variant(variant, $discriminant)
844        };
845    };
846}
847
848#[macro_export]
849macro_rules! define_runtime_enum {
850    (
851        $registry:expr
852        =>
853        $(mod $module_name:ident)?
854        enum $name:ident {
855            $( $variant:tt )*
856        }
857    ) => {{
858        #[allow(unused_mut)]
859        let mut result = $crate::types::enum_type::RuntimeEnumBuilder::new(stringify!($name));
860        $(
861            result = result.module_name(stringify!($module_name).to_owned());
862        )?
863        $( $crate::define_runtime_enum! { @variant $registry => result => $variant } )?
864        result.build()
865    }};
866    (@variant $registry:expr => $result:expr => {
867        $name:ident $( ( $( $field_name:ident : $field_type:ty ),+ ) )? = $discriminant:literal
868    }) => {
869        $result = {
870            #[allow(unused_mut)]
871            let mut variant = $crate::types::enum_type::EnumVariant::new(stringify!($name));
872            $(
873                $(
874                    variant = variant.with_field(
875                        $crate::types::struct_type::StructField::new(
876                            stringify!($field_name),
877                            $registry
878                                .find_type($crate::types::TypeQuery::of::<$field_type>())
879                                .unwrap(),
880                        ),
881                    );
882                )*
883            )?
884            $result.variant_with_discriminant(variant, $discriminant)
885        };
886    };
887    (@variant $registry:expr => $result:expr => {
888        $name:ident $( ( $( $field_name:ident : $field_type:ty ),+ ) )?
889    }) => {
890        $result = {
891            #[allow(unused_mut)]
892            let mut variant = $crate::types::enum_type::EnumVariant::new(stringify!($name));
893            $(
894                $(
895                    variant = variant.with_field(
896                        $crate::types::struct_type::StructField::new(
897                            stringify!($field_name),
898                            $registry
899                                .find_type($crate::types::TypeQuery::of::<$field_type>())
900                                .unwrap(),
901                        ),
902                    );
903                )*
904            )?
905            $result.variant(variant)
906        };
907    };
908    (@variant $registry:expr => $result:expr => {
909        $name:ident $( { $( $field_name:ident : $field_type:ty ),+ } )? = $discriminant:literal
910    }) => {
911        $result = {
912            #[allow(unused_mut)]
913            let mut variant = $crate::types::enum_type::EnumVariant::new(stringify!($name));
914            $(
915                $(
916                    variant = variant.with_field(
917                        $crate::types::struct_type::StructField::new(
918                            stringify!($field_name),
919                            $registry
920                                .find_type($crate::types::TypeQuery::of::<$field_type>())
921                                .unwrap(),
922                        ),
923                    );
924                )*
925            )?
926            $result.variant_with_discriminant(variant, $discriminant)
927        };
928    };
929    (@variant $registry:expr => $result:expr => {
930        $name:ident $( { $( $field_name:ident : $field_type:ty ),+ } )?
931    }) => {
932        $result = {
933            #[allow(unused_mut)]
934            let mut variant = $crate::types::enum_type::EnumVariant::new(stringify!($name));
935            $(
936                $(
937                    variant = variant.with_field(
938                        $crate::types::struct_type::StructField::new(
939                            stringify!($field_name),
940                            $registry
941                                .find_type($crate::types::TypeQuery::of::<$field_type>())
942                                .unwrap(),
943                        ),
944                    );
945                )*
946            )?
947            $result.variant(variant)
948        };
949    };
950}
951
952#[cfg(test)]
953mod test {
954    use crate::{self as intuicio_core};
955    use crate::{IntuicioEnum, meta::Meta, object::*, registry::*};
956    use intuicio_derive::*;
957
958    #[derive(IntuicioEnum, Default)]
959    #[intuicio(meta = "foo")]
960    #[repr(u8)]
961    #[allow(dead_code)]
962    pub enum Bar {
963        #[default]
964        A,
965        B(u8) = 10,
966        C(u16, u32) = 3,
967        D {
968            a: u32,
969            b: u16,
970        },
971    }
972
973    #[intuicio_methods()]
974    impl Bar {
975        #[intuicio_method(meta = "foo")]
976        fn method_meta() {}
977    }
978
979    #[test]
980    fn test_enum_type() {
981        #[repr(u8)]
982        #[allow(dead_code)]
983        #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
984        enum Foo {
985            #[default]
986            A,
987            B(usize),
988            C(u64, u32),
989            D {
990                a: u16,
991                b: u8,
992            },
993        }
994
995        let mut registry = Registry::default().with_basic_types();
996        let a = define_native_enum! {
997            registry => enum (Foo) {
998                {A = 0}
999                {B(a: usize) = 1}
1000                {C(a: u64, b: u32) = 2}
1001                {D { a: u16, b: u8 } = 3}
1002            }
1003        };
1004        let b = define_runtime_enum! {
1005            registry => enum Foo {
1006                {A = 0}
1007                {B(a: usize) = 1}
1008                {C(a: u64, b: u32)}
1009                {D { a: u16, b: u8 }}
1010            }
1011        };
1012        assert!(a.is_compatible(&b));
1013        let enum_type = registry.add_type(a);
1014        assert!(enum_type.is_send());
1015        assert!(enum_type.is_sync());
1016        assert!(enum_type.is_copy());
1017        assert!(enum_type.is_enum());
1018        assert_eq!(enum_type.type_name(), std::any::type_name::<Foo>());
1019        assert_eq!(enum_type.as_enum().unwrap().variants().len(), 4);
1020        assert_eq!(enum_type.as_enum().unwrap().variants()[0].name, "A");
1021        assert_eq!(enum_type.as_enum().unwrap().variants()[0].fields.len(), 0);
1022        assert_eq!(enum_type.as_enum().unwrap().variants()[1].name, "B");
1023        assert_eq!(enum_type.as_enum().unwrap().variants()[1].fields.len(), 1);
1024        assert_eq!(
1025            enum_type.as_enum().unwrap().variants()[1].fields[0].name,
1026            "a"
1027        );
1028        assert_eq!(
1029            enum_type.as_enum().unwrap().variants()[1].fields[0].address_offset(),
1030            8
1031        );
1032        assert_eq!(enum_type.as_enum().unwrap().variants()[2].name, "C");
1033        assert_eq!(enum_type.as_enum().unwrap().variants()[2].fields.len(), 2);
1034        assert_eq!(
1035            enum_type.as_enum().unwrap().variants()[2].fields[0].name,
1036            "a"
1037        );
1038        assert_eq!(
1039            enum_type.as_enum().unwrap().variants()[2].fields[0].address_offset(),
1040            8
1041        );
1042        assert_eq!(
1043            enum_type.as_enum().unwrap().variants()[2].fields[1].name,
1044            "b"
1045        );
1046        assert_eq!(
1047            enum_type.as_enum().unwrap().variants()[2].fields[1].address_offset(),
1048            16
1049        );
1050        assert_eq!(enum_type.as_enum().unwrap().variants()[3].name, "D");
1051        assert_eq!(enum_type.as_enum().unwrap().variants()[3].fields.len(), 2);
1052        assert_eq!(
1053            enum_type.as_enum().unwrap().variants()[3].fields[0].name,
1054            "a"
1055        );
1056        assert_eq!(
1057            enum_type.as_enum().unwrap().variants()[3].fields[0].address_offset(),
1058            2
1059        );
1060        assert_eq!(
1061            enum_type.as_enum().unwrap().variants()[3].fields[1].name,
1062            "b"
1063        );
1064        assert_eq!(
1065            enum_type.as_enum().unwrap().variants()[3].fields[1].address_offset(),
1066            4
1067        );
1068
1069        let source = Foo::D { a: 10, b: 42 };
1070        let mut target = Object::new(enum_type.clone());
1071        assert!(unsafe { !enum_type.try_copy(target.as_ptr(), target.as_mut_ptr()) });
1072        assert_ne!(&source, target.read::<Foo>().unwrap());
1073        assert!(unsafe {
1074            enum_type.try_copy(&source as *const Foo as *const u8, target.as_mut_ptr())
1075        });
1076        assert_eq!(&source, target.read::<Foo>().unwrap());
1077
1078        assert_eq!(
1079            Bar::define_enum(&registry).meta,
1080            Some(Meta::Identifier("foo".to_owned()))
1081        );
1082    }
1083}