1#![allow(unpredictable_function_pointer_comparisons)]
2
3use 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 pub unsafe fn override_send(mut self, mode: bool) -> Self {
296 self.is_send = mode;
297 self
298 }
299
300 pub unsafe fn override_sync(mut self, mode: bool) -> Self {
302 self.is_sync = mode;
303 self
304 }
305
306 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 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 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 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 pub unsafe fn finalize(&self, pointer: *mut ()) {
551 unsafe { (self.finalizer)(pointer) };
552 }
553
554 pub unsafe fn initializer(&self) -> Option<unsafe fn(*mut ())> {
556 self.initializer
557 }
558
559 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(®istry).meta,
1080 Some(Meta::Identifier("foo".to_owned()))
1081 );
1082 }
1083}