FromWorld derive macro (#17352)
simple derive macro for `FromWorld`. Going to be needed for composable pipeline specializers but probably a nice thing to have regardless ## Testing simple manual testing, nothing seemed to blow up. I'm no proc macro pro though, so there's a chance I've mishandled spans somewhere or something.
This commit is contained in:
parent
a64446b77e
commit
a99674ab86
@ -12,11 +12,11 @@ mod world_query;
|
||||
use crate::{query_data::derive_query_data_impl, query_filter::derive_query_filter_impl};
|
||||
use bevy_macro_utils::{derive_label, ensure_no_collision, get_struct_fields, BevyManifest};
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{
|
||||
parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma,
|
||||
ConstParam, DeriveInput, GenericParam, Index, TypeParam,
|
||||
ConstParam, Data, DataStruct, DeriveInput, GenericParam, Index, Token, TypeParam,
|
||||
};
|
||||
|
||||
enum BundleFieldKind {
|
||||
@ -288,7 +288,7 @@ pub fn derive_visit_entities(input: TokenStream) -> TokenStream {
|
||||
pub fn derive_system_param(input: TokenStream) -> TokenStream {
|
||||
let token_stream = input.clone();
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let syn::Data::Struct(syn::DataStruct {
|
||||
let Data::Struct(DataStruct {
|
||||
fields: field_definitions,
|
||||
..
|
||||
}) = ast.data
|
||||
@ -611,3 +611,46 @@ pub fn derive_states(input: TokenStream) -> TokenStream {
|
||||
pub fn derive_substates(input: TokenStream) -> TokenStream {
|
||||
states::derive_substates(input)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromWorld)]
|
||||
pub fn derive_from_world(input: TokenStream) -> TokenStream {
|
||||
let bevy_ecs_path = bevy_ecs_path();
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let struct_name = ast.ident;
|
||||
let (impl_generics, ty_generics, where_clauses) = ast.generics.split_for_impl();
|
||||
|
||||
let Data::Struct(DataStruct { fields, .. }) = &ast.data else {
|
||||
return syn::Error::new(
|
||||
Span::call_site(),
|
||||
"#[derive(FromWorld)]` only supports structs",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into();
|
||||
};
|
||||
|
||||
let field_init_expr = quote!(#bevy_ecs_path::world::FromWorld::from_world(world));
|
||||
|
||||
let field_initializers: Punctuated<TokenStream2, Token![,]> = match fields {
|
||||
syn::Fields::Named(fields_named) => fields_named
|
||||
.named
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let ident = field.ident.clone().unwrap();
|
||||
quote!(#ident: #field_init_expr)
|
||||
})
|
||||
.collect(),
|
||||
syn::Fields::Unnamed(fields_unnamed) => (0..fields_unnamed.unnamed.len())
|
||||
.map(|index| quote!(#index: #field_init_expr))
|
||||
.collect(),
|
||||
syn::Fields::Unit => Punctuated::new(),
|
||||
};
|
||||
|
||||
TokenStream::from(quote! {
|
||||
impl #impl_generics #bevy_ecs_path::world::FromWorld for #struct_name #ty_generics #where_clauses {
|
||||
fn from_world(world: &mut #bevy_ecs_path::world::World) -> Self {
|
||||
#[allow(clippy::init_numbered_fields)]
|
||||
Self { #field_initializers }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ pub use crate::{
|
||||
change_detection::{Mut, Ref, CHECK_TICK_THRESHOLD},
|
||||
world::command_queue::CommandQueue,
|
||||
};
|
||||
pub use bevy_ecs_macros::FromWorld;
|
||||
pub use component_constants::*;
|
||||
pub use deferred_world::DeferredWorld;
|
||||
pub use entity_fetch::WorldEntityFetch;
|
||||
@ -3657,7 +3658,26 @@ unsafe impl Sync for World {}
|
||||
///
|
||||
/// 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`:
|
||||
/// ```rs
|
||||
/// #[derive(Default)]
|
||||
/// struct A;
|
||||
///
|
||||
/// #[derive(Default)]
|
||||
/// struct B(Option<u32>)
|
||||
///
|
||||
/// struct C;
|
||||
///
|
||||
/// impl FromWorld for C {
|
||||
/// fn from_world(_world: &mut World) -> Self {
|
||||
/// Self
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[derive(FromWorld)]
|
||||
/// struct D(A, B, C);
|
||||
/// ```
|
||||
pub trait FromWorld {
|
||||
/// Creates `Self` using data from the given [`World`].
|
||||
fn from_world(world: &mut World) -> Self;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user