DeriveWorld for enums (#17496)
# Objective Fixes #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 <alice.i.cecile@gmail.com> Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>
This commit is contained in:
parent
fd2afeefda
commit
da57dfb62f
@ -612,20 +612,41 @@ pub fn derive_substates(input: TokenStream) -> TokenStream {
|
|||||||
states::derive_substates(input)
|
states::derive_substates(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_derive(FromWorld)]
|
#[proc_macro_derive(FromWorld, attributes(from_world))]
|
||||||
pub fn derive_from_world(input: TokenStream) -> TokenStream {
|
pub fn derive_from_world(input: TokenStream) -> TokenStream {
|
||||||
let bevy_ecs_path = bevy_ecs_path();
|
let bevy_ecs_path = bevy_ecs_path();
|
||||||
let ast = parse_macro_input!(input as DeriveInput);
|
let ast = parse_macro_input!(input as DeriveInput);
|
||||||
let struct_name = ast.ident;
|
let name = ast.ident;
|
||||||
let (impl_generics, ty_generics, where_clauses) = ast.generics.split_for_impl();
|
let (impl_generics, ty_generics, where_clauses) = ast.generics.split_for_impl();
|
||||||
|
|
||||||
let Data::Struct(DataStruct { fields, .. }) = &ast.data else {
|
let (fields, variant_ident) = match &ast.data {
|
||||||
return syn::Error::new(
|
Data::Struct(data) => (&data.fields, None),
|
||||||
Span::call_site(),
|
Data::Enum(data) => {
|
||||||
"#[derive(FromWorld)]` only supports structs",
|
match data.variants.iter().find(|variant| {
|
||||||
)
|
variant
|
||||||
.into_compile_error()
|
.attrs
|
||||||
.into();
|
.iter()
|
||||||
|
.any(|attr| attr.path().is_ident("from_world"))
|
||||||
|
}) {
|
||||||
|
Some(variant) => (&variant.fields, Some(&variant.ident)),
|
||||||
|
None => {
|
||||||
|
return syn::Error::new(
|
||||||
|
Span::call_site(),
|
||||||
|
"No #[from_world] attribute was found on any of this enum's variants.",
|
||||||
|
)
|
||||||
|
.into_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Data::Union(_) => {
|
||||||
|
return syn::Error::new(
|
||||||
|
Span::call_site(),
|
||||||
|
"#[derive(FromWorld)]` does not support unions",
|
||||||
|
)
|
||||||
|
.into_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let field_init_expr = quote!(#bevy_ecs_path::world::FromWorld::from_world(world));
|
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 {
|
|||||||
syn::Fields::Unit => Punctuated::new(),
|
syn::Fields::Unit => Punctuated::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let field_initializers: TokenStream2 = if !field_initializers.is_empty() {
|
||||||
|
quote!({ #field_initializers })
|
||||||
|
} else {
|
||||||
|
quote!(#field_initializers)
|
||||||
|
};
|
||||||
|
|
||||||
|
let field_initializers = match variant_ident {
|
||||||
|
Some(variant_ident) => quote!( Self::#variant_ident #field_initializers),
|
||||||
|
None => quote!( Self #field_initializers),
|
||||||
|
};
|
||||||
|
|
||||||
TokenStream::from(quote! {
|
TokenStream::from(quote! {
|
||||||
impl #impl_generics #bevy_ecs_path::world::FromWorld for #struct_name #ty_generics #where_clauses {
|
impl #impl_generics #bevy_ecs_path::world::FromWorld for #name #ty_generics #where_clauses {
|
||||||
fn from_world(world: &mut #bevy_ecs_path::world::World) -> Self {
|
fn from_world(world: &mut #bevy_ecs_path::world::World) -> Self {
|
||||||
#[allow(clippy::init_numbered_fields)]
|
#[allow(clippy::init_numbered_fields)]
|
||||||
Self { #field_initializers }
|
#field_initializers
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -3716,9 +3716,13 @@ unsafe impl Sync for World {}
|
|||||||
///
|
///
|
||||||
/// This can be helpful for complex initialization or context-aware defaults.
|
/// This can be helpful for complex initialization or context-aware defaults.
|
||||||
///
|
///
|
||||||
/// [`FromWorld`] is automatically implemented for any type implementing [`Default`],
|
/// [`FromWorld`] is automatically implemented for any type implementing [`Default`]
|
||||||
/// and may also be derived for any struct whose fields all implement `FromWorld`:
|
/// and may also be derived for:
|
||||||
|
/// - any struct whose fields all implement `FromWorld`
|
||||||
|
/// - any enum where one variant has the attribute `#[from_world]`
|
||||||
|
///
|
||||||
/// ```rs
|
/// ```rs
|
||||||
|
///
|
||||||
/// #[derive(Default)]
|
/// #[derive(Default)]
|
||||||
/// struct A;
|
/// struct A;
|
||||||
///
|
///
|
||||||
@ -3735,6 +3739,13 @@ unsafe impl Sync for World {}
|
|||||||
///
|
///
|
||||||
/// #[derive(FromWorld)]
|
/// #[derive(FromWorld)]
|
||||||
/// struct D(A, B, C);
|
/// struct D(A, B, C);
|
||||||
|
///
|
||||||
|
/// #[derive(FromWorld)]
|
||||||
|
/// enum E {
|
||||||
|
/// #[from_world]
|
||||||
|
/// F,
|
||||||
|
/// G
|
||||||
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub trait FromWorld {
|
pub trait FromWorld {
|
||||||
/// Creates `Self` using data from the given [`World`].
|
/// Creates `Self` using data from the given [`World`].
|
||||||
|
Loading…
Reference in New Issue
Block a user