Allow #[derive(Bundle)] on tuple structs (take 3) (#10561)
- rework of old @Veykril's work in [2499](https://github.com/bevyengine/bevy/pull/2499) - Fixes [3537](https://github.com/bevyengine/bevy/issues/3537)
This commit is contained in:
parent
7ff61a8dc9
commit
eeb0c2f2e4
@ -5,7 +5,7 @@ mod fetch;
|
|||||||
mod states;
|
mod states;
|
||||||
|
|
||||||
use crate::fetch::derive_world_query_impl;
|
use crate::fetch::derive_world_query_impl;
|
||||||
use bevy_macro_utils::{derive_label, ensure_no_collision, get_named_struct_fields, BevyManifest};
|
use bevy_macro_utils::{derive_label, ensure_no_collision, get_struct_fields, BevyManifest};
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
@ -27,8 +27,8 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
|||||||
let ast = parse_macro_input!(input as DeriveInput);
|
let ast = parse_macro_input!(input as DeriveInput);
|
||||||
let ecs_path = bevy_ecs_path();
|
let ecs_path = bevy_ecs_path();
|
||||||
|
|
||||||
let named_fields = match get_named_struct_fields(&ast.data) {
|
let named_fields = match get_struct_fields(&ast.data) {
|
||||||
Ok(fields) => &fields.named,
|
Ok(fields) => fields,
|
||||||
Err(e) => return e.into_compile_error().into(),
|
Err(e) => return e.into_compile_error().into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,8 +59,9 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
let field = named_fields
|
let field = named_fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|field| field.ident.as_ref().unwrap())
|
.map(|field| field.ident.as_ref())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let field_type = named_fields
|
let field_type = named_fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|field| &field.ty)
|
.map(|field| &field.ty)
|
||||||
@ -69,14 +70,19 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
|||||||
let mut field_component_ids = Vec::new();
|
let mut field_component_ids = Vec::new();
|
||||||
let mut field_get_components = Vec::new();
|
let mut field_get_components = Vec::new();
|
||||||
let mut field_from_components = Vec::new();
|
let mut field_from_components = Vec::new();
|
||||||
for ((field_type, field_kind), field) in
|
for (((i, field_type), field_kind), field) in field_type
|
||||||
field_type.iter().zip(field_kind.iter()).zip(field.iter())
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.zip(field_kind.iter())
|
||||||
|
.zip(field.iter())
|
||||||
{
|
{
|
||||||
match field_kind {
|
match field_kind {
|
||||||
BundleFieldKind::Component => {
|
BundleFieldKind::Component => {
|
||||||
field_component_ids.push(quote! {
|
field_component_ids.push(quote! {
|
||||||
<#field_type as #ecs_path::bundle::Bundle>::component_ids(components, storages, &mut *ids);
|
<#field_type as #ecs_path::bundle::Bundle>::component_ids(components, storages, &mut *ids);
|
||||||
});
|
});
|
||||||
|
match field {
|
||||||
|
Some(field) => {
|
||||||
field_get_components.push(quote! {
|
field_get_components.push(quote! {
|
||||||
self.#field.get_components(&mut *func);
|
self.#field.get_components(&mut *func);
|
||||||
});
|
});
|
||||||
@ -84,6 +90,17 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
|||||||
#field: <#field_type as #ecs_path::bundle::Bundle>::from_components(ctx, &mut *func),
|
#field: <#field_type as #ecs_path::bundle::Bundle>::from_components(ctx, &mut *func),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
|
let index = syn::Index::from(i);
|
||||||
|
field_get_components.push(quote! {
|
||||||
|
self.#index.get_components(&mut *func);
|
||||||
|
});
|
||||||
|
field_from_components.push(quote! {
|
||||||
|
#index: <#field_type as #ecs_path::bundle::Bundle>::from_components(ctx, &mut *func),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BundleFieldKind::Ignore => {
|
BundleFieldKind::Ignore => {
|
||||||
field_from_components.push(quote! {
|
field_from_components.push(quote! {
|
||||||
|
@ -1724,4 +1724,22 @@ mod tests {
|
|||||||
"new entity was spawned and received C component"
|
"new entity was spawned and received C component"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct ComponentA(u32);
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct ComponentB(u32);
|
||||||
|
|
||||||
|
#[derive(Bundle)]
|
||||||
|
struct Simple(ComponentA);
|
||||||
|
|
||||||
|
#[derive(Bundle)]
|
||||||
|
struct Tuple(Simple, ComponentB);
|
||||||
|
|
||||||
|
#[derive(Bundle)]
|
||||||
|
struct Record {
|
||||||
|
field0: Simple,
|
||||||
|
field1: ComponentB,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
use proc_macro::Span;
|
use proc_macro::Span;
|
||||||
use syn::{Data, DataStruct, Error, Fields, FieldsNamed};
|
use syn::{punctuated::Punctuated, token::Comma, Data, DataStruct, Error, Field, Fields};
|
||||||
|
|
||||||
/// Get the fields of a data structure if that structure is a struct with named fields;
|
/// Get the fields of a data structure if that structure is a struct with named fields;
|
||||||
/// otherwise, return a compile error that points to the site of the macro invocation.
|
/// otherwise, return a compile error that points to the site of the macro invocation.
|
||||||
pub fn get_named_struct_fields(data: &syn::Data) -> syn::Result<&FieldsNamed> {
|
pub fn get_struct_fields(data: &syn::Data) -> syn::Result<&Punctuated<Field, Comma>> {
|
||||||
match data {
|
match data {
|
||||||
Data::Struct(DataStruct {
|
Data::Struct(DataStruct {
|
||||||
fields: Fields::Named(fields),
|
fields: Fields::Named(fields),
|
||||||
..
|
..
|
||||||
}) => Ok(fields),
|
}) => Ok(&fields.named),
|
||||||
|
Data::Struct(DataStruct {
|
||||||
|
fields: Fields::Unnamed(fields),
|
||||||
|
..
|
||||||
|
}) => Ok(&fields.unnamed),
|
||||||
_ => Err(Error::new(
|
_ => Err(Error::new(
|
||||||
// This deliberately points to the call site rather than the structure
|
// This deliberately points to the call site rather than the structure
|
||||||
// body; marking the entire body as the source of the error makes it
|
// body; marking the entire body as the source of the error makes it
|
||||||
// impossible to figure out which `derive` has a problem.
|
// impossible to figure out which `derive` has a problem.
|
||||||
Span::call_site().into(),
|
Span::call_site().into(),
|
||||||
"Only structs with named fields are supported",
|
"Only structs are supported",
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user