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,
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::<Self, #ident>(
storages,
required_components,
|| { let x: #ident = #func().into(); x },
inheritance_depth
);
});
} else {
register_required.push(quote! {
components.register_required_components_manual::<Self, #ident>(
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::<Self, #ident>(
storages,
required_components,
|| { let x: #ident = #func().into(); x },
inheritance_depth
);
});
}
Some(RequireFunc::Closure(func)) => {
register_required.push(quote! {
components.register_required_components_manual::<Self, #ident>(
storages,
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 {
path: Path,
func: Option<Path>,
func: Option<RequireFunc>,
}
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::<Path>()?;
Some(func)
if let Ok(func) = content.parse::<ExprClosure>() {
Some(RequireFunc::Closure(func))
} else {
let func = content.parse::<Path>()?;
Some(RequireFunc::Path(func))
}
} else {
None
};

View File

@ -146,25 +146,33 @@ use thiserror::Error;
/// 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::*;
/// #[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;
///
/// fn init_b() -> B {
/// B(10)
/// #[derive(Component, PartialEq, Eq, Debug)]
/// struct C(usize);
///
/// 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::<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,
@ -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();