Skip to content

Commit

Permalink
Allow a closure to be used as a required component default (#15269)
Browse files Browse the repository at this point in the history
# 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.
  • Loading branch information
fluffiac authored Oct 4, 2024
1 parent 20dbf79 commit f0704cf
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 39 deletions.
66 changes: 44 additions & 22 deletions crates/bevy_ecs/macros/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
);
});
}
}
}
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
};
Expand Down
34 changes: 17 additions & 17 deletions crates/bevy_ecs/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
///
/// #[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::<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,
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit f0704cf

Please sign in to comment.