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:
parent
20dbf790a6
commit
f0704cffa4
@ -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
|
||||
};
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user