bevy_ecs/system/
builder.rs

1use alloc::{boxed::Box, vec::Vec};
2use bevy_platform::cell::SyncCell;
3use variadics_please::all_tuples;
4
5use 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};
18use core::fmt::Debug;
19
20use 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`].
112pub 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)]
164pub struct ParamBuilder;
165
166// SAFETY: Calls `SystemParam::init_state`
167unsafe impl<P: SystemParam> SystemParamBuilder<P> for ParamBuilder {
168    fn build(self, world: &mut World) -> P::State {
169        P::init_state(world)
170    }
171}
172
173impl 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`.
209unsafe 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)]
259pub struct QueryParamBuilder<T>(T);
260
261impl<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
271impl<'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`.
283unsafe 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
298macro_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
327all_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
337unsafe 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)]
417pub struct ParamSetBuilder<T>(pub T);
418
419macro_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
443all_tuples!(impl_param_set_builder_tuple, 1, 8, P, B);
444
445// SAFETY: implementors of each `SystemParamBuilder` in the vec have validated their impls
446unsafe 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.
459pub struct DynParamBuilder<'a>(Box<dyn FnOnce(&mut World) -> DynSystemParamState + 'a>);
460
461impl<'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.
474unsafe 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)]
500pub struct LocalBuilder<T>(pub T);
501
502// SAFETY: Any value of `T` is a valid state for `Local`.
503unsafe 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)]
514pub struct FilteredResourcesParamBuilder<T>(T);
515
516impl<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
526impl<'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`.
535unsafe 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)]
548pub struct FilteredResourcesMutParamBuilder<T>(T);
549
550impl<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
560impl<'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`.
569unsafe 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)]
581pub 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>`
584unsafe 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)]
594pub 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>`
597unsafe 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)]
610pub 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>`
613unsafe 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)]
620mod 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}