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;
|
||||
|
||||
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_macro2::Span;
|
||||
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 ecs_path = bevy_ecs_path();
|
||||
|
||||
let named_fields = match get_named_struct_fields(&ast.data) {
|
||||
Ok(fields) => &fields.named,
|
||||
let named_fields = match get_struct_fields(&ast.data) {
|
||||
Ok(fields) => fields,
|
||||
Err(e) => return e.into_compile_error().into(),
|
||||
};
|
||||
|
||||
@ -59,8 +59,9 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
||||
|
||||
let field = named_fields
|
||||
.iter()
|
||||
.map(|field| field.ident.as_ref().unwrap())
|
||||
.map(|field| field.ident.as_ref())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let field_type = named_fields
|
||||
.iter()
|
||||
.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_get_components = Vec::new();
|
||||
let mut field_from_components = Vec::new();
|
||||
for ((field_type, field_kind), field) in
|
||||
field_type.iter().zip(field_kind.iter()).zip(field.iter())
|
||||
for (((i, field_type), field_kind), field) in field_type
|
||||
.iter()
|
||||
.enumerate()
|
||||
.zip(field_kind.iter())
|
||||
.zip(field.iter())
|
||||
{
|
||||
match field_kind {
|
||||
BundleFieldKind::Component => {
|
||||
field_component_ids.push(quote! {
|
||||
<#field_type as #ecs_path::bundle::Bundle>::component_ids(components, storages, &mut *ids);
|
||||
});
|
||||
match field {
|
||||
Some(field) => {
|
||||
field_get_components.push(quote! {
|
||||
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),
|
||||
});
|
||||
}
|
||||
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 => {
|
||||
field_from_components.push(quote! {
|
||||
@ -115,7 +132,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
||||
where
|
||||
__F: FnMut(&mut __T) -> #ecs_path::ptr::OwningPtr<'_>
|
||||
{
|
||||
Self {
|
||||
Self{
|
||||
#(#field_from_components)*
|
||||
}
|
||||
}
|
||||
|
@ -1724,4 +1724,22 @@ mod tests {
|
||||
"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 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;
|
||||
/// 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 {
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Named(fields),
|
||||
..
|
||||
}) => Ok(fields),
|
||||
}) => Ok(&fields.named),
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Unnamed(fields),
|
||||
..
|
||||
}) => Ok(&fields.unnamed),
|
||||
_ => Err(Error::new(
|
||||
// This deliberately points to the call site rather than the structure
|
||||
// body; marking the entire body as the source of the error makes it
|
||||
// impossible to figure out which `derive` has a problem.
|
||||
Span::call_site().into(),
|
||||
"Only structs with named fields are supported",
|
||||
"Only structs are supported",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user