Skip to content

Commit 0a28298

Browse files
Bleachfuelalice-i-cecileBenjaminBrienen
authored andcommitted
DeriveWorld for enums (bevyengine#17496)
# Objective Fixes bevyengine#17457 ## Solution #[derive(FromWorld)] now works with enums by specifying which variant should be used. ## Showcase ```rust #[Derive(FromWorld)] enum Game { #[from_world] Playing, Stopped } ``` --------- Co-authored-by: Alice Cecile <[email protected]> Co-authored-by: Benjamin Brienen <[email protected]>
1 parent ef9e06b commit 0a28298

File tree

2 files changed

+59
-16
lines changed

2 files changed

+59
-16
lines changed

crates/bevy_ecs/macros/src/lib.rs

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -612,20 +612,41 @@ pub fn derive_substates(input: TokenStream) -> TokenStream {
612612
states::derive_substates(input)
613613
}
614614

615-
#[proc_macro_derive(FromWorld)]
615+
#[proc_macro_derive(FromWorld, attributes(from_world))]
616616
pub fn derive_from_world(input: TokenStream) -> TokenStream {
617617
let bevy_ecs_path = bevy_ecs_path();
618618
let ast = parse_macro_input!(input as DeriveInput);
619-
let struct_name = ast.ident;
619+
let name = ast.ident;
620620
let (impl_generics, ty_generics, where_clauses) = ast.generics.split_for_impl();
621621

622-
let Data::Struct(DataStruct { fields, .. }) = &ast.data else {
623-
return syn::Error::new(
624-
Span::call_site(),
625-
"#[derive(FromWorld)]` only supports structs",
626-
)
627-
.into_compile_error()
628-
.into();
622+
let (fields, variant_ident) = match &ast.data {
623+
Data::Struct(data) => (&data.fields, None),
624+
Data::Enum(data) => {
625+
match data.variants.iter().find(|variant| {
626+
variant
627+
.attrs
628+
.iter()
629+
.any(|attr| attr.path().is_ident("from_world"))
630+
}) {
631+
Some(variant) => (&variant.fields, Some(&variant.ident)),
632+
None => {
633+
return syn::Error::new(
634+
Span::call_site(),
635+
"No #[from_world] attribute was found on any of this enum's variants.",
636+
)
637+
.into_compile_error()
638+
.into();
639+
}
640+
}
641+
}
642+
Data::Union(_) => {
643+
return syn::Error::new(
644+
Span::call_site(),
645+
"#[derive(FromWorld)]` does not support unions",
646+
)
647+
.into_compile_error()
648+
.into();
649+
}
629650
};
630651

631652
let field_init_expr = quote!(#bevy_ecs_path::world::FromWorld::from_world(world));
@@ -645,12 +666,23 @@ pub fn derive_from_world(input: TokenStream) -> TokenStream {
645666
syn::Fields::Unit => Punctuated::new(),
646667
};
647668

669+
let field_initializers: TokenStream2 = if !field_initializers.is_empty() {
670+
quote!({ #field_initializers })
671+
} else {
672+
quote!(#field_initializers)
673+
};
674+
675+
let field_initializers = match variant_ident {
676+
Some(variant_ident) => quote!( Self::#variant_ident #field_initializers),
677+
None => quote!( Self #field_initializers),
678+
};
679+
648680
TokenStream::from(quote! {
649-
impl #impl_generics #bevy_ecs_path::world::FromWorld for #struct_name #ty_generics #where_clauses {
650-
fn from_world(world: &mut #bevy_ecs_path::world::World) -> Self {
651-
#[allow(clippy::init_numbered_fields)]
652-
Self { #field_initializers }
681+
impl #impl_generics #bevy_ecs_path::world::FromWorld for #name #ty_generics #where_clauses {
682+
fn from_world(world: &mut #bevy_ecs_path::world::World) -> Self {
683+
#[allow(clippy::init_numbered_fields)]
684+
#field_initializers
685+
}
653686
}
654-
}
655687
})
656688
}

crates/bevy_ecs/src/world/mod.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3716,9 +3716,13 @@ unsafe impl Sync for World {}
37163716
///
37173717
/// This can be helpful for complex initialization or context-aware defaults.
37183718
///
3719-
/// [`FromWorld`] is automatically implemented for any type implementing [`Default`],
3720-
/// and may also be derived for any struct whose fields all implement `FromWorld`:
3719+
/// [`FromWorld`] is automatically implemented for any type implementing [`Default`]
3720+
/// and may also be derived for:
3721+
/// - any struct whose fields all implement `FromWorld`
3722+
/// - any enum where one variant has the attribute `#[from_world]`
3723+
///
37213724
/// ```rs
3725+
///
37223726
/// #[derive(Default)]
37233727
/// struct A;
37243728
///
@@ -3735,6 +3739,13 @@ unsafe impl Sync for World {}
37353739
///
37363740
/// #[derive(FromWorld)]
37373741
/// struct D(A, B, C);
3742+
///
3743+
/// #[derive(FromWorld)]
3744+
/// enum E {
3745+
/// #[from_world]
3746+
/// F,
3747+
/// G
3748+
/// }
37383749
/// ```
37393750
pub trait FromWorld {
37403751
/// Creates `Self` using data from the given [`World`].

0 commit comments

Comments
 (0)