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.
This commit is contained in:
fluffiac 2024-10-03 18:34:39 -06:00 committed by GitHub
parent 20dbf790a6
commit f0704cffa4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 61 additions and 39 deletions

View File

@ -9,7 +9,7 @@ use syn::{
punctuated::Punctuated, punctuated::Punctuated,
spanned::Spanned, spanned::Spanned,
token::{Comma, Paren}, token::{Comma, Paren},
DeriveInput, ExprPath, Ident, LitStr, Path, Result, DeriveInput, ExprClosure, ExprPath, Ident, LitStr, Path, Result,
}; };
pub fn derive_event(input: TokenStream) -> TokenStream { pub fn derive_event(input: TokenStream) -> TokenStream {
@ -90,24 +90,37 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
inheritance_depth + 1 inheritance_depth + 1
); );
}); });
if let Some(func) = &require.func { match &require.func {
register_required.push(quote! { Some(RequireFunc::Path(func)) => {
components.register_required_components_manual::<Self, #ident>( register_required.push(quote! {
storages, components.register_required_components_manual::<Self, #ident>(
required_components, storages,
|| { let x: #ident = #func().into(); x }, required_components,
inheritance_depth || { let x: #ident = #func().into(); x },
); inheritance_depth
}); );
} else { });
register_required.push(quote! { }
components.register_required_components_manual::<Self, #ident>( Some(RequireFunc::Closure(func)) => {
storages, register_required.push(quote! {
required_components, components.register_required_components_manual::<Self, #ident>(
<#ident as Default>::default, storages,
inheritance_depth required_components,
); || { let x: #ident = (#func)().into(); x },
}); inheritance_depth
);
});
}
None => {
register_required.push(quote! {
components.register_required_components_manual::<Self, #ident>(
storages,
required_components,
<#ident as Default>::default,
inheritance_depth
);
});
}
} }
} }
} }
@ -180,7 +193,12 @@ enum StorageTy {
struct Require { struct Require {
path: Path, path: Path,
func: Option<Path>, func: Option<RequireFunc>,
}
enum RequireFunc {
Path(Path),
Closure(ExprClosure),
} }
// values for `storage` attribute // values for `storage` attribute
@ -256,8 +274,12 @@ impl Parse for Require {
let func = if input.peek(Paren) { let func = if input.peek(Paren) {
let content; let content;
parenthesized!(content in input); parenthesized!(content in input);
let func = content.parse::<Path>()?; if let Ok(func) = content.parse::<ExprClosure>() {
Some(func) Some(RequireFunc::Closure(func))
} else {
let func = content.parse::<Path>()?;
Some(RequireFunc::Path(func))
}
} else { } else {
None None
}; };

View File

@ -146,25 +146,33 @@ use thiserror::Error;
/// assert_eq!(&C(0), world.entity(id).get::<C>().unwrap()); /// assert_eq!(&C(0), world.entity(id).get::<C>().unwrap());
/// ``` /// ```
/// ///
/// You can also define a custom constructor: /// You can also define a custom constructor function or closure:
/// ///
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// #[derive(Component)] /// #[derive(Component)]
/// #[require(B(init_b))] /// #[require(C(init_c))]
/// struct A; /// struct A;
/// ///
/// #[derive(Component, PartialEq, Eq, Debug)] /// #[derive(Component, PartialEq, Eq, Debug)]
/// struct B(usize); /// #[require(C(|| C(20)))]
/// struct B;
/// ///
/// fn init_b() -> B { /// #[derive(Component, PartialEq, Eq, Debug)]
/// B(10) /// struct C(usize);
///
/// fn init_c() -> C {
/// C(10)
/// } /// }
/// ///
/// # let mut world = World::default(); /// # 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(); /// let id = world.spawn(A).id();
/// assert_eq!(&B(10), world.entity(id).get::<B>().unwrap()); /// assert_eq!(&C(10), world.entity(id).get::<C>().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::<C>().unwrap());
/// ``` /// ```
/// ///
/// Required components are _recursive_. This means, if a Required Component has required components, /// Required components are _recursive_. This means, if a Required Component has required components,
@ -202,24 +210,16 @@ use thiserror::Error;
/// struct X(usize); /// struct X(usize);
/// ///
/// #[derive(Component, Default)] /// #[derive(Component, Default)]
/// #[require(X(x1))] /// #[require(X(|| X(1)))]
/// struct Y; /// struct Y;
/// ///
/// fn x1() -> X {
/// X(1)
/// }
///
/// #[derive(Component)] /// #[derive(Component)]
/// #[require( /// #[require(
/// Y, /// Y,
/// X(x2), /// X(|| X(2)),
/// )] /// )]
/// struct Z; /// struct Z;
/// ///
/// fn x2() -> X {
/// X(2)
/// }
///
/// # let mut world = World::default(); /// # let mut world = World::default();
/// // In this case, the x2 constructor is used for X /// // In this case, the x2 constructor is used for X
/// let id = world.spawn(Z).id(); /// let id = world.spawn(Z).id();