Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/system/builder.rs
6604 views
1
use alloc::{boxed::Box, vec::Vec};
2
use bevy_platform::cell::SyncCell;
3
use variadics_please::all_tuples;
4
5
use crate::{
6
prelude::QueryBuilder,
7
query::{QueryData, QueryFilter, QueryState},
8
resource::Resource,
9
system::{
10
DynSystemParam, DynSystemParamState, If, Local, ParamSet, Query, SystemParam,
11
SystemParamValidationError,
12
},
13
world::{
14
FilteredResources, FilteredResourcesBuilder, FilteredResourcesMut,
15
FilteredResourcesMutBuilder, FromWorld, World,
16
},
17
};
18
use core::fmt::Debug;
19
20
use super::{Res, ResMut, SystemState};
21
22
/// A builder that can create a [`SystemParam`].
23
///
24
/// ```
25
/// # use bevy_ecs::{
26
/// # prelude::*,
27
/// # system::{SystemParam, ParamBuilder},
28
/// # };
29
/// # #[derive(Resource)]
30
/// # struct R;
31
/// #
32
/// # #[derive(SystemParam)]
33
/// # struct MyParam;
34
/// #
35
/// fn some_system(param: MyParam) {}
36
///
37
/// fn build_system(builder: impl SystemParamBuilder<MyParam>) {
38
/// let mut world = World::new();
39
/// // To build a system, create a tuple of `SystemParamBuilder`s
40
/// // with a builder for each parameter.
41
/// // Note that the builder for a system must be a tuple,
42
/// // even if there is only one parameter.
43
/// (builder,)
44
/// .build_state(&mut world)
45
/// .build_system(some_system);
46
/// }
47
///
48
/// fn build_closure_system_infer(builder: impl SystemParamBuilder<MyParam>) {
49
/// let mut world = World::new();
50
/// // Closures can be used in addition to named functions.
51
/// // If a closure is used, the parameter types must all be inferred
52
/// // from the builders, so you cannot use plain `ParamBuilder`.
53
/// (builder, ParamBuilder::resource())
54
/// .build_state(&mut world)
55
/// .build_system(|param, res| {
56
/// let param: MyParam = param;
57
/// let res: Res<R> = res;
58
/// });
59
/// }
60
///
61
/// fn build_closure_system_explicit(builder: impl SystemParamBuilder<MyParam>) {
62
/// let mut world = World::new();
63
/// // Alternately, you can provide all types in the closure
64
/// // parameter list and call `build_any_system()`.
65
/// (builder, ParamBuilder)
66
/// .build_state(&mut world)
67
/// .build_any_system(|param: MyParam, res: Res<R>| {});
68
/// }
69
/// ```
70
///
71
/// See the documentation for individual builders for more examples.
72
///
73
/// # List of Builders
74
///
75
/// [`ParamBuilder`] can be used for parameters that don't require any special building.
76
/// Using a `ParamBuilder` will build the system parameter the same way it would be initialized in an ordinary system.
77
///
78
/// `ParamBuilder` also provides factory methods that return a `ParamBuilder` typed as `impl SystemParamBuilder<P>`
79
/// for common system parameters that can be used to guide closure parameter inference.
80
///
81
/// [`QueryParamBuilder`] can build a [`Query`] to add additional filters,
82
/// or to configure the components available to [`FilteredEntityRef`](crate::world::FilteredEntityRef) or [`FilteredEntityMut`](crate::world::FilteredEntityMut).
83
/// You can also use a [`QueryState`] to build a [`Query`].
84
///
85
/// [`LocalBuilder`] can build a [`Local`] to supply the initial value for the `Local`.
86
///
87
/// [`FilteredResourcesParamBuilder`] can build a [`FilteredResources`],
88
/// and [`FilteredResourcesMutParamBuilder`] can build a [`FilteredResourcesMut`],
89
/// to configure the resources that can be accessed.
90
///
91
/// [`DynParamBuilder`] can build a [`DynSystemParam`] to determine the type of the inner parameter,
92
/// and to supply any `SystemParamBuilder` it needs.
93
///
94
/// Tuples of builders can build tuples of parameters, one builder for each element.
95
/// Note that since systems require a tuple as a parameter, the outer builder for a system will always be a tuple.
96
///
97
/// A [`Vec`] of builders can build a `Vec` of parameters, one builder for each element.
98
///
99
/// A [`ParamSetBuilder`] can build a [`ParamSet`].
100
/// This can wrap either a tuple or a `Vec`, one builder for each element.
101
///
102
/// A custom system param created with `#[derive(SystemParam)]` can be buildable if it includes a `#[system_param(builder)]` attribute.
103
/// See [the documentation for `SystemParam` derives](SystemParam#builders).
104
///
105
/// # Safety
106
///
107
/// The implementor must ensure that the state returned
108
/// from [`SystemParamBuilder::build`] is valid for `P`.
109
/// Note that the exact safety requirements depend on the implementation of [`SystemParam`],
110
/// so if `Self` is not a local type then you must call [`SystemParam::init_state`]
111
/// or another [`SystemParamBuilder::build`].
112
pub unsafe trait SystemParamBuilder<P: SystemParam>: Sized {
113
/// Registers any [`World`] access used by this [`SystemParam`]
114
/// and creates a new instance of this param's [`State`](SystemParam::State).
115
fn build(self, world: &mut World) -> P::State;
116
117
/// Create a [`SystemState`] from a [`SystemParamBuilder`].
118
/// To create a system, call [`SystemState::build_system`] on the result.
119
fn build_state(self, world: &mut World) -> SystemState<P> {
120
SystemState::from_builder(world, self)
121
}
122
}
123
124
/// A [`SystemParamBuilder`] for any [`SystemParam`] that uses its default initialization.
125
///
126
/// ## Example
127
///
128
/// ```
129
/// # use bevy_ecs::{
130
/// # prelude::*,
131
/// # system::{SystemParam, ParamBuilder},
132
/// # };
133
/// #
134
/// # #[derive(Component)]
135
/// # struct A;
136
/// #
137
/// # #[derive(Resource)]
138
/// # struct R;
139
/// #
140
/// # #[derive(SystemParam)]
141
/// # struct MyParam;
142
/// #
143
/// # let mut world = World::new();
144
/// # world.insert_resource(R);
145
/// #
146
/// fn my_system(res: Res<R>, param: MyParam, query: Query<&A>) {
147
/// // ...
148
/// }
149
///
150
/// let system = (
151
/// // A plain ParamBuilder can build any parameter type.
152
/// ParamBuilder,
153
/// // The `of::<P>()` method returns a `ParamBuilder`
154
/// // typed as `impl SystemParamBuilder<P>`.
155
/// ParamBuilder::of::<MyParam>(),
156
/// // The other factory methods return typed builders
157
/// // for common parameter types.
158
/// ParamBuilder::query::<&A>(),
159
/// )
160
/// .build_state(&mut world)
161
/// .build_system(my_system);
162
/// ```
163
#[derive(Default, Debug, Clone)]
164
pub struct ParamBuilder;
165
166
// SAFETY: Calls `SystemParam::init_state`
167
unsafe impl<P: SystemParam> SystemParamBuilder<P> for ParamBuilder {
168
fn build(self, world: &mut World) -> P::State {
169
P::init_state(world)
170
}
171
}
172
173
impl ParamBuilder {
174
/// Creates a [`SystemParamBuilder`] for any [`SystemParam`] that uses its default initialization.
175
pub fn of<T: SystemParam>() -> impl SystemParamBuilder<T> {
176
Self
177
}
178
179
/// Helper method for reading a [`Resource`] as a param, equivalent to `of::<Res<T>>()`
180
pub fn resource<'w, T: Resource>() -> impl SystemParamBuilder<Res<'w, T>> {
181
Self
182
}
183
184
/// Helper method for mutably accessing a [`Resource`] as a param, equivalent to `of::<ResMut<T>>()`
185
pub fn resource_mut<'w, T: Resource>() -> impl SystemParamBuilder<ResMut<'w, T>> {
186
Self
187
}
188
189
/// Helper method for adding a [`Local`] as a param, equivalent to `of::<Local<T>>()`
190
pub fn local<'s, T: FromWorld + Send + 'static>() -> impl SystemParamBuilder<Local<'s, T>> {
191
Self
192
}
193
194
/// Helper method for adding a [`Query`] as a param, equivalent to `of::<Query<D>>()`
195
pub fn query<'w, 's, D: QueryData + 'static>() -> impl SystemParamBuilder<Query<'w, 's, D, ()>>
196
{
197
Self
198
}
199
200
/// Helper method for adding a filtered [`Query`] as a param, equivalent to `of::<Query<D, F>>()`
201
pub fn query_filtered<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>(
202
) -> impl SystemParamBuilder<Query<'w, 's, D, F>> {
203
Self
204
}
205
}
206
207
// SAFETY: Any `QueryState<D, F>` for the correct world is valid for `Query::State`,
208
// and we check the world during `build`.
209
unsafe impl<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>
210
SystemParamBuilder<Query<'w, 's, D, F>> for QueryState<D, F>
211
{
212
fn build(self, world: &mut World) -> QueryState<D, F> {
213
self.validate_world(world.id());
214
self
215
}
216
}
217
218
/// A [`SystemParamBuilder`] for a [`Query`].
219
/// This takes a closure accepting an `&mut` [`QueryBuilder`] and uses the builder to construct the query's state.
220
/// This can be used to add additional filters,
221
/// or to configure the components available to [`FilteredEntityRef`](crate::world::FilteredEntityRef) or [`FilteredEntityMut`](crate::world::FilteredEntityMut).
222
///
223
/// ## Example
224
///
225
/// ```
226
/// # use bevy_ecs::{
227
/// # prelude::*,
228
/// # system::{SystemParam, QueryParamBuilder},
229
/// # };
230
/// #
231
/// # #[derive(Component)]
232
/// # struct Player;
233
/// #
234
/// # let mut world = World::new();
235
/// let system = (QueryParamBuilder::new(|builder| {
236
/// builder.with::<Player>();
237
/// }),)
238
/// .build_state(&mut world)
239
/// .build_system(|query: Query<()>| {
240
/// for _ in &query {
241
/// // This only includes entities with a `Player` component.
242
/// }
243
/// });
244
///
245
/// // When collecting multiple builders into a `Vec`,
246
/// // use `new_box()` to erase the closure type.
247
/// let system = (vec![
248
/// QueryParamBuilder::new_box(|builder| {
249
/// builder.with::<Player>();
250
/// }),
251
/// QueryParamBuilder::new_box(|builder| {
252
/// builder.without::<Player>();
253
/// }),
254
/// ],)
255
/// .build_state(&mut world)
256
/// .build_system(|query: Vec<Query<()>>| {});
257
/// ```
258
#[derive(Clone)]
259
pub struct QueryParamBuilder<T>(T);
260
261
impl<T> QueryParamBuilder<T> {
262
/// Creates a [`SystemParamBuilder`] for a [`Query`] that accepts a callback to configure the [`QueryBuilder`].
263
pub fn new<D: QueryData, F: QueryFilter>(f: T) -> Self
264
where
265
T: FnOnce(&mut QueryBuilder<D, F>),
266
{
267
Self(f)
268
}
269
}
270
271
impl<'a, D: QueryData, F: QueryFilter>
272
QueryParamBuilder<Box<dyn FnOnce(&mut QueryBuilder<D, F>) + 'a>>
273
{
274
/// Creates a [`SystemParamBuilder`] for a [`Query`] that accepts a callback to configure the [`QueryBuilder`].
275
/// This boxes the callback so that it has a common type and can be put in a `Vec`.
276
pub fn new_box(f: impl FnOnce(&mut QueryBuilder<D, F>) + 'a) -> Self {
277
Self(Box::new(f))
278
}
279
}
280
281
// SAFETY: Any `QueryState<D, F>` for the correct world is valid for `Query::State`,
282
// and `QueryBuilder` produces one with the given `world`.
283
unsafe impl<
284
'w,
285
's,
286
D: QueryData + 'static,
287
F: QueryFilter + 'static,
288
T: FnOnce(&mut QueryBuilder<D, F>),
289
> SystemParamBuilder<Query<'w, 's, D, F>> for QueryParamBuilder<T>
290
{
291
fn build(self, world: &mut World) -> QueryState<D, F> {
292
let mut builder = QueryBuilder::new(world);
293
(self.0)(&mut builder);
294
builder.build()
295
}
296
}
297
298
macro_rules! impl_system_param_builder_tuple {
299
($(#[$meta:meta])* $(($param: ident, $builder: ident)),*) => {
300
#[expect(
301
clippy::allow_attributes,
302
reason = "This is in a macro; as such, the below lints may not always apply."
303
)]
304
#[allow(
305
unused_variables,
306
reason = "Zero-length tuples won't use any of the parameters."
307
)]
308
#[allow(
309
non_snake_case,
310
reason = "The variable names are provided by the macro caller, not by us."
311
)]
312
$(#[$meta])*
313
// SAFETY: implementors of each `SystemParamBuilder` in the tuple have validated their impls
314
unsafe impl<$($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<($($param,)*)> for ($($builder,)*) {
315
fn build(self, world: &mut World) -> <($($param,)*) as SystemParam>::State {
316
let ($($builder,)*) = self;
317
#[allow(
318
clippy::unused_unit,
319
reason = "Zero-length tuples won't generate any calls to the system parameter builders."
320
)]
321
($($builder.build(world),)*)
322
}
323
}
324
};
325
}
326
327
all_tuples!(
328
#[doc(fake_variadic)]
329
impl_system_param_builder_tuple,
330
0,
331
16,
332
P,
333
B
334
);
335
336
// SAFETY: implementors of each `SystemParamBuilder` in the vec have validated their impls
337
unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<Vec<P>> for Vec<B> {
338
fn build(self, world: &mut World) -> <Vec<P> as SystemParam>::State {
339
self.into_iter()
340
.map(|builder| builder.build(world))
341
.collect()
342
}
343
}
344
345
/// A [`SystemParamBuilder`] for a [`ParamSet`].
346
///
347
/// To build a [`ParamSet`] with a tuple of system parameters, pass a tuple of matching [`SystemParamBuilder`]s.
348
/// To build a [`ParamSet`] with a [`Vec`] of system parameters, pass a `Vec` of matching [`SystemParamBuilder`]s.
349
///
350
/// # Examples
351
///
352
/// ```
353
/// # use bevy_ecs::{prelude::*, system::*};
354
/// #
355
/// # #[derive(Component)]
356
/// # struct Health;
357
/// #
358
/// # #[derive(Component)]
359
/// # struct Enemy;
360
/// #
361
/// # #[derive(Component)]
362
/// # struct Ally;
363
/// #
364
/// # let mut world = World::new();
365
/// #
366
/// let system = (ParamSetBuilder((
367
/// QueryParamBuilder::new(|builder| {
368
/// builder.with::<Enemy>();
369
/// }),
370
/// QueryParamBuilder::new(|builder| {
371
/// builder.with::<Ally>();
372
/// }),
373
/// ParamBuilder,
374
/// )),)
375
/// .build_state(&mut world)
376
/// .build_system(buildable_system_with_tuple);
377
/// # world.run_system_once(system);
378
///
379
/// fn buildable_system_with_tuple(
380
/// mut set: ParamSet<(Query<&mut Health>, Query<&mut Health>, &World)>,
381
/// ) {
382
/// // The first parameter is built from the first builder,
383
/// // so this will iterate over enemies.
384
/// for mut health in set.p0().iter_mut() {}
385
/// // And the second parameter is built from the second builder,
386
/// // so this will iterate over allies.
387
/// for mut health in set.p1().iter_mut() {}
388
/// // Parameters that don't need special building can use `ParamBuilder`.
389
/// let entities = set.p2().entities();
390
/// }
391
///
392
/// let system = (ParamSetBuilder(vec![
393
/// QueryParamBuilder::new_box(|builder| {
394
/// builder.with::<Enemy>();
395
/// }),
396
/// QueryParamBuilder::new_box(|builder| {
397
/// builder.with::<Ally>();
398
/// }),
399
/// ]),)
400
/// .build_state(&mut world)
401
/// .build_system(buildable_system_with_vec);
402
/// # world.run_system_once(system);
403
///
404
/// fn buildable_system_with_vec(mut set: ParamSet<Vec<Query<&mut Health>>>) {
405
/// // As with tuples, the first parameter is built from the first builder,
406
/// // so this will iterate over enemies.
407
/// for mut health in set.get_mut(0).iter_mut() {}
408
/// // And the second parameter is built from the second builder,
409
/// // so this will iterate over allies.
410
/// for mut health in set.get_mut(1).iter_mut() {}
411
/// // You can iterate over the parameters either by index,
412
/// // or using the `for_each` method.
413
/// set.for_each(|mut query| for mut health in query.iter_mut() {});
414
/// }
415
/// ```
416
#[derive(Debug, Default, Clone)]
417
pub struct ParamSetBuilder<T>(pub T);
418
419
macro_rules! impl_param_set_builder_tuple {
420
($(($param: ident, $builder: ident)),*) => {
421
#[expect(
422
clippy::allow_attributes,
423
reason = "This is in a macro; as such, the below lints may not always apply."
424
)]
425
#[allow(
426
unused_variables,
427
reason = "Zero-length tuples won't use any of the parameters."
428
)]
429
#[allow(
430
non_snake_case,
431
reason = "The variable names are provided by the macro caller, not by us."
432
)]
433
// SAFETY: implementors of each `SystemParamBuilder` in the tuple have validated their impls
434
unsafe impl<'w, 's, $($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<ParamSet<'w, 's, ($($param,)*)>> for ParamSetBuilder<($($builder,)*)> {
435
fn build(self, world: &mut World) -> <($($param,)*) as SystemParam>::State {
436
let ParamSetBuilder(($($builder,)*)) = self;
437
($($builder.build(world),)*)
438
}
439
}
440
};
441
}
442
443
all_tuples!(impl_param_set_builder_tuple, 1, 8, P, B);
444
445
// SAFETY: implementors of each `SystemParamBuilder` in the vec have validated their impls
446
unsafe impl<'w, 's, P: SystemParam, B: SystemParamBuilder<P>>
447
SystemParamBuilder<ParamSet<'w, 's, Vec<P>>> for ParamSetBuilder<Vec<B>>
448
{
449
fn build(self, world: &mut World) -> <Vec<P> as SystemParam>::State {
450
self.0
451
.into_iter()
452
.map(|builder| builder.build(world))
453
.collect()
454
}
455
}
456
457
/// A [`SystemParamBuilder`] for a [`DynSystemParam`].
458
/// See the [`DynSystemParam`] docs for examples.
459
pub struct DynParamBuilder<'a>(Box<dyn FnOnce(&mut World) -> DynSystemParamState + 'a>);
460
461
impl<'a> DynParamBuilder<'a> {
462
/// Creates a new [`DynParamBuilder`] by wrapping a [`SystemParamBuilder`] of any type.
463
/// The built [`DynSystemParam`] can be downcast to `T`.
464
pub fn new<T: SystemParam + 'static>(builder: impl SystemParamBuilder<T> + 'a) -> Self {
465
Self(Box::new(|world| {
466
DynSystemParamState::new::<T>(builder.build(world))
467
}))
468
}
469
}
470
471
// SAFETY: `DynSystemParam::get_param` will call `get_param` on the boxed `DynSystemParamState`,
472
// and the boxed builder was a valid implementation of `SystemParamBuilder` for that type.
473
// The resulting `DynSystemParam` can only perform access by downcasting to that param type.
474
unsafe impl<'a, 'w, 's> SystemParamBuilder<DynSystemParam<'w, 's>> for DynParamBuilder<'a> {
475
fn build(self, world: &mut World) -> <DynSystemParam<'w, 's> as SystemParam>::State {
476
(self.0)(world)
477
}
478
}
479
480
/// A [`SystemParamBuilder`] for a [`Local`].
481
/// The provided value will be used as the initial value of the `Local`.
482
///
483
/// ## Example
484
///
485
/// ```
486
/// # use bevy_ecs::{
487
/// # prelude::*,
488
/// # system::{SystemParam, LocalBuilder, RunSystemOnce},
489
/// # };
490
/// #
491
/// # let mut world = World::new();
492
/// let system = (LocalBuilder(100),)
493
/// .build_state(&mut world)
494
/// .build_system(|local: Local<usize>| {
495
/// assert_eq!(*local, 100);
496
/// });
497
/// # world.run_system_once(system);
498
/// ```
499
#[derive(Default, Debug, Clone)]
500
pub struct LocalBuilder<T>(pub T);
501
502
// SAFETY: Any value of `T` is a valid state for `Local`.
503
unsafe impl<'s, T: FromWorld + Send + 'static> SystemParamBuilder<Local<'s, T>>
504
for LocalBuilder<T>
505
{
506
fn build(self, _world: &mut World) -> <Local<'s, T> as SystemParam>::State {
507
SyncCell::new(self.0)
508
}
509
}
510
511
/// A [`SystemParamBuilder`] for a [`FilteredResources`].
512
/// See the [`FilteredResources`] docs for examples.
513
#[derive(Clone)]
514
pub struct FilteredResourcesParamBuilder<T>(T);
515
516
impl<T> FilteredResourcesParamBuilder<T> {
517
/// Creates a [`SystemParamBuilder`] for a [`FilteredResources`] that accepts a callback to configure the [`FilteredResourcesBuilder`].
518
pub fn new(f: T) -> Self
519
where
520
T: FnOnce(&mut FilteredResourcesBuilder),
521
{
522
Self(f)
523
}
524
}
525
526
impl<'a> FilteredResourcesParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesBuilder) + 'a>> {
527
/// Creates a [`SystemParamBuilder`] for a [`FilteredResources`] that accepts a callback to configure the [`FilteredResourcesBuilder`].
528
/// This boxes the callback so that it has a common type.
529
pub fn new_box(f: impl FnOnce(&mut FilteredResourcesBuilder) + 'a) -> Self {
530
Self(Box::new(f))
531
}
532
}
533
534
// SAFETY: Any `Access` is a valid state for `FilteredResources`.
535
unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesBuilder)>
536
SystemParamBuilder<FilteredResources<'w, 's>> for FilteredResourcesParamBuilder<T>
537
{
538
fn build(self, world: &mut World) -> <FilteredResources<'w, 's> as SystemParam>::State {
539
let mut builder = FilteredResourcesBuilder::new(world);
540
(self.0)(&mut builder);
541
builder.build()
542
}
543
}
544
545
/// A [`SystemParamBuilder`] for a [`FilteredResourcesMut`].
546
/// See the [`FilteredResourcesMut`] docs for examples.
547
#[derive(Clone)]
548
pub struct FilteredResourcesMutParamBuilder<T>(T);
549
550
impl<T> FilteredResourcesMutParamBuilder<T> {
551
/// Creates a [`SystemParamBuilder`] for a [`FilteredResourcesMut`] that accepts a callback to configure the [`FilteredResourcesMutBuilder`].
552
pub fn new(f: T) -> Self
553
where
554
T: FnOnce(&mut FilteredResourcesMutBuilder),
555
{
556
Self(f)
557
}
558
}
559
560
impl<'a> FilteredResourcesMutParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesMutBuilder) + 'a>> {
561
/// Creates a [`SystemParamBuilder`] for a [`FilteredResourcesMut`] that accepts a callback to configure the [`FilteredResourcesMutBuilder`].
562
/// This boxes the callback so that it has a common type.
563
pub fn new_box(f: impl FnOnce(&mut FilteredResourcesMutBuilder) + 'a) -> Self {
564
Self(Box::new(f))
565
}
566
}
567
568
// SAFETY: Any `Access` is a valid state for `FilteredResourcesMut`.
569
unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesMutBuilder)>
570
SystemParamBuilder<FilteredResourcesMut<'w, 's>> for FilteredResourcesMutParamBuilder<T>
571
{
572
fn build(self, world: &mut World) -> <FilteredResourcesMut<'w, 's> as SystemParam>::State {
573
let mut builder = FilteredResourcesMutBuilder::new(world);
574
(self.0)(&mut builder);
575
builder.build()
576
}
577
}
578
579
/// A [`SystemParamBuilder`] for an [`Option`].
580
#[derive(Clone)]
581
pub struct OptionBuilder<T>(T);
582
583
// SAFETY: `OptionBuilder<B>` builds a state that is valid for `P`, and any state valid for `P` is valid for `Option<P>`
584
unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<Option<P>>
585
for OptionBuilder<B>
586
{
587
fn build(self, world: &mut World) -> <Option<P> as SystemParam>::State {
588
self.0.build(world)
589
}
590
}
591
592
/// A [`SystemParamBuilder`] for a [`Result`] of [`SystemParamValidationError`].
593
#[derive(Clone)]
594
pub struct ResultBuilder<T>(T);
595
596
// SAFETY: `ResultBuilder<B>` builds a state that is valid for `P`, and any state valid for `P` is valid for `Result<P, SystemParamValidationError>`
597
unsafe impl<P: SystemParam, B: SystemParamBuilder<P>>
598
SystemParamBuilder<Result<P, SystemParamValidationError>> for ResultBuilder<B>
599
{
600
fn build(
601
self,
602
world: &mut World,
603
) -> <Result<P, SystemParamValidationError> as SystemParam>::State {
604
self.0.build(world)
605
}
606
}
607
608
/// A [`SystemParamBuilder`] for a [`If`].
609
#[derive(Clone)]
610
pub struct IfBuilder<T>(T);
611
612
// SAFETY: `IfBuilder<B>` builds a state that is valid for `P`, and any state valid for `P` is valid for `If<P>`
613
unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<If<P>> for IfBuilder<B> {
614
fn build(self, world: &mut World) -> <If<P> as SystemParam>::State {
615
self.0.build(world)
616
}
617
}
618
619
#[cfg(test)]
620
mod tests {
621
use crate::{
622
entity::Entities,
623
error::Result,
624
prelude::{Component, Query},
625
reflect::ReflectResource,
626
system::{Local, RunSystemOnce},
627
};
628
use alloc::vec;
629
use bevy_reflect::{FromType, Reflect, ReflectRef};
630
631
use super::*;
632
633
#[derive(Component)]
634
struct A;
635
636
#[derive(Component)]
637
struct B;
638
639
#[derive(Component)]
640
struct C;
641
642
#[derive(Resource, Default, Reflect)]
643
#[reflect(Resource)]
644
struct R {
645
foo: usize,
646
}
647
648
fn local_system(local: Local<u64>) -> u64 {
649
*local
650
}
651
652
fn query_system(query: Query<()>) -> usize {
653
query.iter().count()
654
}
655
656
fn query_system_result(query: Query<()>) -> Result<usize> {
657
Ok(query.iter().count())
658
}
659
660
fn multi_param_system(a: Local<u64>, b: Local<u64>) -> u64 {
661
*a + *b + 1
662
}
663
664
#[test]
665
fn local_builder() {
666
let mut world = World::new();
667
668
let system = (LocalBuilder(10),)
669
.build_state(&mut world)
670
.build_system(local_system);
671
672
let output = world.run_system_once(system).unwrap();
673
assert_eq!(output, 10);
674
}
675
676
#[test]
677
fn query_builder() {
678
let mut world = World::new();
679
680
world.spawn(A);
681
world.spawn_empty();
682
683
let system = (QueryParamBuilder::new(|query| {
684
query.with::<A>();
685
}),)
686
.build_state(&mut world)
687
.build_system(query_system);
688
689
let output = world.run_system_once(system).unwrap();
690
assert_eq!(output, 1);
691
}
692
693
#[test]
694
fn query_builder_result_fallible() {
695
let mut world = World::new();
696
697
world.spawn(A);
698
world.spawn_empty();
699
700
let system = (QueryParamBuilder::new(|query| {
701
query.with::<A>();
702
}),)
703
.build_state(&mut world)
704
.build_system(query_system_result);
705
706
// The type annotation here is necessary since the system
707
// could also return `Result<usize>`
708
let output: usize = world.run_system_once(system).unwrap();
709
assert_eq!(output, 1);
710
}
711
712
#[test]
713
fn query_builder_result_infallible() {
714
let mut world = World::new();
715
716
world.spawn(A);
717
world.spawn_empty();
718
719
let system = (QueryParamBuilder::new(|query| {
720
query.with::<A>();
721
}),)
722
.build_state(&mut world)
723
.build_system(query_system_result);
724
725
// The type annotation here is necessary since the system
726
// could also return `usize`
727
let output: Result<usize> = world.run_system_once(system).unwrap();
728
assert_eq!(output.unwrap(), 1);
729
}
730
731
#[test]
732
fn query_builder_state() {
733
let mut world = World::new();
734
735
world.spawn(A);
736
world.spawn_empty();
737
738
let state = QueryBuilder::new(&mut world).with::<A>().build();
739
740
let system = (state,).build_state(&mut world).build_system(query_system);
741
742
let output = world.run_system_once(system).unwrap();
743
assert_eq!(output, 1);
744
}
745
746
#[test]
747
fn multi_param_builder() {
748
let mut world = World::new();
749
750
world.spawn(A);
751
world.spawn_empty();
752
753
let system = (LocalBuilder(0), ParamBuilder)
754
.build_state(&mut world)
755
.build_system(multi_param_system);
756
757
let output = world.run_system_once(system).unwrap();
758
assert_eq!(output, 1);
759
}
760
761
#[test]
762
fn vec_builder() {
763
let mut world = World::new();
764
765
world.spawn((A, B, C));
766
world.spawn((A, B));
767
world.spawn((A, C));
768
world.spawn((A, C));
769
world.spawn_empty();
770
771
let system = (vec![
772
QueryParamBuilder::new_box(|builder| {
773
builder.with::<B>().without::<C>();
774
}),
775
QueryParamBuilder::new_box(|builder| {
776
builder.with::<C>().without::<B>();
777
}),
778
],)
779
.build_state(&mut world)
780
.build_system(|params: Vec<Query<&mut A>>| {
781
let mut count: usize = 0;
782
params
783
.into_iter()
784
.for_each(|mut query| count += query.iter_mut().count());
785
count
786
});
787
788
let output = world.run_system_once(system).unwrap();
789
assert_eq!(output, 3);
790
}
791
792
#[test]
793
fn multi_param_builder_inference() {
794
let mut world = World::new();
795
796
world.spawn(A);
797
world.spawn_empty();
798
799
let system = (LocalBuilder(0u64), ParamBuilder::local::<u64>())
800
.build_state(&mut world)
801
.build_system(|a, b| *a + *b + 1);
802
803
let output = world.run_system_once(system).unwrap();
804
assert_eq!(output, 1);
805
}
806
807
#[test]
808
fn param_set_builder() {
809
let mut world = World::new();
810
811
world.spawn((A, B, C));
812
world.spawn((A, B));
813
world.spawn((A, C));
814
world.spawn((A, C));
815
world.spawn_empty();
816
817
let system = (ParamSetBuilder((
818
QueryParamBuilder::new(|builder| {
819
builder.with::<B>();
820
}),
821
QueryParamBuilder::new(|builder| {
822
builder.with::<C>();
823
}),
824
)),)
825
.build_state(&mut world)
826
.build_system(|mut params: ParamSet<(Query<&mut A>, Query<&mut A>)>| {
827
params.p0().iter().count() + params.p1().iter().count()
828
});
829
830
let output = world.run_system_once(system).unwrap();
831
assert_eq!(output, 5);
832
}
833
834
#[test]
835
fn param_set_vec_builder() {
836
let mut world = World::new();
837
838
world.spawn((A, B, C));
839
world.spawn((A, B));
840
world.spawn((A, C));
841
world.spawn((A, C));
842
world.spawn_empty();
843
844
let system = (ParamSetBuilder(vec![
845
QueryParamBuilder::new_box(|builder| {
846
builder.with::<B>();
847
}),
848
QueryParamBuilder::new_box(|builder| {
849
builder.with::<C>();
850
}),
851
]),)
852
.build_state(&mut world)
853
.build_system(|mut params: ParamSet<Vec<Query<&mut A>>>| {
854
let mut count = 0;
855
params.for_each(|mut query| count += query.iter_mut().count());
856
count
857
});
858
859
let output = world.run_system_once(system).unwrap();
860
assert_eq!(output, 5);
861
}
862
863
#[test]
864
fn dyn_builder() {
865
let mut world = World::new();
866
867
world.spawn(A);
868
world.spawn_empty();
869
870
let system = (
871
DynParamBuilder::new(LocalBuilder(3_usize)),
872
DynParamBuilder::new::<Query<()>>(QueryParamBuilder::new(|builder| {
873
builder.with::<A>();
874
})),
875
DynParamBuilder::new::<&Entities>(ParamBuilder),
876
)
877
.build_state(&mut world)
878
.build_system(
879
|mut p0: DynSystemParam, mut p1: DynSystemParam, mut p2: DynSystemParam| {
880
let local = *p0.downcast_mut::<Local<usize>>().unwrap();
881
let query_count = p1.downcast_mut::<Query<()>>().unwrap().iter().count();
882
let _entities = p2.downcast_mut::<&Entities>().unwrap();
883
assert!(p0.downcast_mut::<Query<()>>().is_none());
884
local + query_count
885
},
886
);
887
888
let output = world.run_system_once(system).unwrap();
889
assert_eq!(output, 4);
890
}
891
892
#[derive(SystemParam)]
893
#[system_param(builder)]
894
struct CustomParam<'w, 's> {
895
query: Query<'w, 's, ()>,
896
local: Local<'s, usize>,
897
}
898
899
#[test]
900
fn custom_param_builder() {
901
let mut world = World::new();
902
903
world.spawn(A);
904
world.spawn_empty();
905
906
let system = (CustomParamBuilder {
907
local: LocalBuilder(100),
908
query: QueryParamBuilder::new(|builder| {
909
builder.with::<A>();
910
}),
911
},)
912
.build_state(&mut world)
913
.build_system(|param: CustomParam| *param.local + param.query.iter().count());
914
915
let output = world.run_system_once(system).unwrap();
916
assert_eq!(output, 101);
917
}
918
919
#[test]
920
fn filtered_resource_conflicts_read_with_res() {
921
let mut world = World::new();
922
(
923
ParamBuilder::resource(),
924
FilteredResourcesParamBuilder::new(|builder| {
925
builder.add_read::<R>();
926
}),
927
)
928
.build_state(&mut world)
929
.build_system(|_r: Res<R>, _fr: FilteredResources| {});
930
}
931
932
#[test]
933
#[should_panic]
934
fn filtered_resource_conflicts_read_with_resmut() {
935
let mut world = World::new();
936
(
937
ParamBuilder::resource_mut(),
938
FilteredResourcesParamBuilder::new(|builder| {
939
builder.add_read::<R>();
940
}),
941
)
942
.build_state(&mut world)
943
.build_system(|_r: ResMut<R>, _fr: FilteredResources| {});
944
}
945
946
#[test]
947
#[should_panic]
948
fn filtered_resource_conflicts_read_all_with_resmut() {
949
let mut world = World::new();
950
(
951
ParamBuilder::resource_mut(),
952
FilteredResourcesParamBuilder::new(|builder| {
953
builder.add_read_all();
954
}),
955
)
956
.build_state(&mut world)
957
.build_system(|_r: ResMut<R>, _fr: FilteredResources| {});
958
}
959
960
#[test]
961
fn filtered_resource_mut_conflicts_read_with_res() {
962
let mut world = World::new();
963
(
964
ParamBuilder::resource(),
965
FilteredResourcesMutParamBuilder::new(|builder| {
966
builder.add_read::<R>();
967
}),
968
)
969
.build_state(&mut world)
970
.build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
971
}
972
973
#[test]
974
#[should_panic]
975
fn filtered_resource_mut_conflicts_read_with_resmut() {
976
let mut world = World::new();
977
(
978
ParamBuilder::resource_mut(),
979
FilteredResourcesMutParamBuilder::new(|builder| {
980
builder.add_read::<R>();
981
}),
982
)
983
.build_state(&mut world)
984
.build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
985
}
986
987
#[test]
988
#[should_panic]
989
fn filtered_resource_mut_conflicts_write_with_res() {
990
let mut world = World::new();
991
(
992
ParamBuilder::resource(),
993
FilteredResourcesMutParamBuilder::new(|builder| {
994
builder.add_write::<R>();
995
}),
996
)
997
.build_state(&mut world)
998
.build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
999
}
1000
1001
#[test]
1002
#[should_panic]
1003
fn filtered_resource_mut_conflicts_write_all_with_res() {
1004
let mut world = World::new();
1005
(
1006
ParamBuilder::resource(),
1007
FilteredResourcesMutParamBuilder::new(|builder| {
1008
builder.add_write_all();
1009
}),
1010
)
1011
.build_state(&mut world)
1012
.build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1013
}
1014
1015
#[test]
1016
#[should_panic]
1017
fn filtered_resource_mut_conflicts_write_with_resmut() {
1018
let mut world = World::new();
1019
(
1020
ParamBuilder::resource_mut(),
1021
FilteredResourcesMutParamBuilder::new(|builder| {
1022
builder.add_write::<R>();
1023
}),
1024
)
1025
.build_state(&mut world)
1026
.build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
1027
}
1028
1029
#[test]
1030
fn filtered_resource_reflect() {
1031
let mut world = World::new();
1032
world.insert_resource(R { foo: 7 });
1033
1034
let system = (FilteredResourcesParamBuilder::new(|builder| {
1035
builder.add_read::<R>();
1036
}),)
1037
.build_state(&mut world)
1038
.build_system(|res: FilteredResources| {
1039
let reflect_resource = <ReflectResource as FromType<R>>::from_type();
1040
let ReflectRef::Struct(reflect_struct) =
1041
reflect_resource.reflect(res).unwrap().reflect_ref()
1042
else {
1043
panic!()
1044
};
1045
*reflect_struct
1046
.field("foo")
1047
.unwrap()
1048
.try_downcast_ref::<usize>()
1049
.unwrap()
1050
});
1051
1052
let output = world.run_system_once(system).unwrap();
1053
assert_eq!(output, 7);
1054
}
1055
}
1056
1057