From f0704cffa440f6270281ca68f702cf387f8bd74e Mon Sep 17 00:00:00 2001 From: fluffiac Date: Thu, 3 Oct 2024 18:34:39 -0600 Subject: [PATCH] Allow a closure to be used as a required component default (#15269) # Objective Allow required component default values to be provided in-line. ```rust #[derive(Component)] #[require( FocusPolicy(block_focus_policy) )] struct SomeComponent; fn block_focus_policy() -> FocusPolicy { FocusPolicy::Block } ``` May now be expressed as: ```rust #[derive(Component)] #[require( FocusPolicy(|| FocusPolicy::Block) )] struct SomeComponent; ``` ## Solution Modified the #[require] proc macro to accept a closure. ## Testing Tested using my branch as a dependency, and switching between the inline closure syntax and function syntax for a bunch of different components. --- crates/bevy_ecs/macros/src/component.rs | 66 ++++++++++++++++--------- crates/bevy_ecs/src/component.rs | 34 ++++++------- 2 files changed, 61 insertions(+), 39 deletions(-) diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index de41596f97a8e..08b4e73056635 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -9,7 +9,7 @@ use syn::{ punctuated::Punctuated, spanned::Spanned, token::{Comma, Paren}, - DeriveInput, ExprPath, Ident, LitStr, Path, Result, + DeriveInput, ExprClosure, ExprPath, Ident, LitStr, Path, Result, }; pub fn derive_event(input: TokenStream) -> TokenStream { @@ -90,24 +90,37 @@ pub fn derive_component(input: TokenStream) -> TokenStream { inheritance_depth + 1 ); }); - if let Some(func) = &require.func { - register_required.push(quote! { - components.register_required_components_manual::( - storages, - required_components, - || { let x: #ident = #func().into(); x }, - inheritance_depth - ); - }); - } else { - register_required.push(quote! { - components.register_required_components_manual::( - storages, - required_components, - <#ident as Default>::default, - inheritance_depth - ); - }); + match &require.func { + Some(RequireFunc::Path(func)) => { + register_required.push(quote! { + components.register_required_components_manual::( + storages, + required_components, + || { let x: #ident = #func().into(); x }, + inheritance_depth + ); + }); + } + Some(RequireFunc::Closure(func)) => { + register_required.push(quote! { + components.register_required_components_manual::( + storages, + required_components, + || { let x: #ident = (#func)().into(); x }, + inheritance_depth + ); + }); + } + None => { + register_required.push(quote! { + components.register_required_components_manual::( + storages, + required_components, + <#ident as Default>::default, + inheritance_depth + ); + }); + } } } } @@ -180,7 +193,12 @@ enum StorageTy { struct Require { path: Path, - func: Option, + func: Option, +} + +enum RequireFunc { + Path(Path), + Closure(ExprClosure), } // values for `storage` attribute @@ -256,8 +274,12 @@ impl Parse for Require { let func = if input.peek(Paren) { let content; parenthesized!(content in input); - let func = content.parse::()?; - Some(func) + if let Ok(func) = content.parse::() { + Some(RequireFunc::Closure(func)) + } else { + let func = content.parse::()?; + Some(RequireFunc::Path(func)) + } } else { None }; diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 121d49a291985..5c443e8bcc1c5 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -146,25 +146,33 @@ use thiserror::Error; /// assert_eq!(&C(0), world.entity(id).get::().unwrap()); /// ``` /// -/// You can also define a custom constructor: +/// You can also define a custom constructor function or closure: /// /// ``` /// # use bevy_ecs::prelude::*; /// #[derive(Component)] -/// #[require(B(init_b))] +/// #[require(C(init_c))] /// struct A; /// /// #[derive(Component, PartialEq, Eq, Debug)] -/// struct B(usize); +/// #[require(C(|| C(20)))] +/// struct B; +/// +/// #[derive(Component, PartialEq, Eq, Debug)] +/// struct C(usize); /// -/// fn init_b() -> B { -/// B(10) +/// fn init_c() -> C { +/// C(10) /// } /// /// # let mut world = World::default(); -/// // This will implicitly also insert B with the init_b() constructor +/// // This will implicitly also insert C with the init_c() constructor /// let id = world.spawn(A).id(); -/// assert_eq!(&B(10), world.entity(id).get::().unwrap()); +/// assert_eq!(&C(10), world.entity(id).get::().unwrap()); +/// +/// // This will implicitly also insert C with the `|| C(20)` constructor closure +/// let id = world.spawn(B).id(); +/// assert_eq!(&C(20), world.entity(id).get::().unwrap()); /// ``` /// /// Required components are _recursive_. This means, if a Required Component has required components, @@ -202,24 +210,16 @@ use thiserror::Error; /// struct X(usize); /// /// #[derive(Component, Default)] -/// #[require(X(x1))] +/// #[require(X(|| X(1)))] /// struct Y; /// -/// fn x1() -> X { -/// X(1) -/// } -/// /// #[derive(Component)] /// #[require( /// Y, -/// X(x2), +/// X(|| X(2)), /// )] /// struct Z; /// -/// fn x2() -> X { -/// X(2) -/// } -/// /// # let mut world = World::default(); /// // In this case, the x2 constructor is used for X /// let id = world.spawn(Z).id();