Skip to content

Commit f0704cf

Browse files
authored
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.
1 parent 20dbf79 commit f0704cf

File tree

2 files changed

+61
-39
lines changed

2 files changed

+61
-39
lines changed

crates/bevy_ecs/macros/src/component.rs

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use syn::{
99
punctuated::Punctuated,
1010
spanned::Spanned,
1111
token::{Comma, Paren},
12-
DeriveInput, ExprPath, Ident, LitStr, Path, Result,
12+
DeriveInput, ExprClosure, ExprPath, Ident, LitStr, Path, Result,
1313
};
1414

1515
pub fn derive_event(input: TokenStream) -> TokenStream {
@@ -90,24 +90,37 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
9090
inheritance_depth + 1
9191
);
9292
});
93-
if let Some(func) = &require.func {
94-
register_required.push(quote! {
95-
components.register_required_components_manual::<Self, #ident>(
96-
storages,
97-
required_components,
98-
|| { let x: #ident = #func().into(); x },
99-
inheritance_depth
100-
);
101-
});
102-
} else {
103-
register_required.push(quote! {
104-
components.register_required_components_manual::<Self, #ident>(
105-
storages,
106-
required_components,
107-
<#ident as Default>::default,
108-
inheritance_depth
109-
);
110-
});
93+
match &require.func {
94+
Some(RequireFunc::Path(func)) => {
95+
register_required.push(quote! {
96+
components.register_required_components_manual::<Self, #ident>(
97+
storages,
98+
required_components,
99+
|| { let x: #ident = #func().into(); x },
100+
inheritance_depth
101+
);
102+
});
103+
}
104+
Some(RequireFunc::Closure(func)) => {
105+
register_required.push(quote! {
106+
components.register_required_components_manual::<Self, #ident>(
107+
storages,
108+
required_components,
109+
|| { let x: #ident = (#func)().into(); x },
110+
inheritance_depth
111+
);
112+
});
113+
}
114+
None => {
115+
register_required.push(quote! {
116+
components.register_required_components_manual::<Self, #ident>(
117+
storages,
118+
required_components,
119+
<#ident as Default>::default,
120+
inheritance_depth
121+
);
122+
});
123+
}
111124
}
112125
}
113126
}
@@ -180,7 +193,12 @@ enum StorageTy {
180193

181194
struct Require {
182195
path: Path,
183-
func: Option<Path>,
196+
func: Option<RequireFunc>,
197+
}
198+
199+
enum RequireFunc {
200+
Path(Path),
201+
Closure(ExprClosure),
184202
}
185203

186204
// values for `storage` attribute
@@ -256,8 +274,12 @@ impl Parse for Require {
256274
let func = if input.peek(Paren) {
257275
let content;
258276
parenthesized!(content in input);
259-
let func = content.parse::<Path>()?;
260-
Some(func)
277+
if let Ok(func) = content.parse::<ExprClosure>() {
278+
Some(RequireFunc::Closure(func))
279+
} else {
280+
let func = content.parse::<Path>()?;
281+
Some(RequireFunc::Path(func))
282+
}
261283
} else {
262284
None
263285
};

crates/bevy_ecs/src/component.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -146,25 +146,33 @@ use thiserror::Error;
146146
/// assert_eq!(&C(0), world.entity(id).get::<C>().unwrap());
147147
/// ```
148148
///
149-
/// You can also define a custom constructor:
149+
/// You can also define a custom constructor function or closure:
150150
///
151151
/// ```
152152
/// # use bevy_ecs::prelude::*;
153153
/// #[derive(Component)]
154-
/// #[require(B(init_b))]
154+
/// #[require(C(init_c))]
155155
/// struct A;
156156
///
157157
/// #[derive(Component, PartialEq, Eq, Debug)]
158-
/// struct B(usize);
158+
/// #[require(C(|| C(20)))]
159+
/// struct B;
160+
///
161+
/// #[derive(Component, PartialEq, Eq, Debug)]
162+
/// struct C(usize);
159163
///
160-
/// fn init_b() -> B {
161-
/// B(10)
164+
/// fn init_c() -> C {
165+
/// C(10)
162166
/// }
163167
///
164168
/// # let mut world = World::default();
165-
/// // This will implicitly also insert B with the init_b() constructor
169+
/// // This will implicitly also insert C with the init_c() constructor
166170
/// let id = world.spawn(A).id();
167-
/// assert_eq!(&B(10), world.entity(id).get::<B>().unwrap());
171+
/// assert_eq!(&C(10), world.entity(id).get::<C>().unwrap());
172+
///
173+
/// // This will implicitly also insert C with the `|| C(20)` constructor closure
174+
/// let id = world.spawn(B).id();
175+
/// assert_eq!(&C(20), world.entity(id).get::<C>().unwrap());
168176
/// ```
169177
///
170178
/// Required components are _recursive_. This means, if a Required Component has required components,
@@ -202,24 +210,16 @@ use thiserror::Error;
202210
/// struct X(usize);
203211
///
204212
/// #[derive(Component, Default)]
205-
/// #[require(X(x1))]
213+
/// #[require(X(|| X(1)))]
206214
/// struct Y;
207215
///
208-
/// fn x1() -> X {
209-
/// X(1)
210-
/// }
211-
///
212216
/// #[derive(Component)]
213217
/// #[require(
214218
/// Y,
215-
/// X(x2),
219+
/// X(|| X(2)),
216220
/// )]
217221
/// struct Z;
218222
///
219-
/// fn x2() -> X {
220-
/// X(2)
221-
/// }
222-
///
223223
/// # let mut world = World::default();
224224
/// // In this case, the x2 constructor is used for X
225225
/// let id = world.spawn(Z).id();

0 commit comments

Comments
 (0)