bevy_ecs/system/
builder.rs

1use bevy_utils::{all_tuples, synccell::SyncCell};
2
3use crate::{
4    prelude::QueryBuilder,
5    query::{QueryData, QueryFilter, QueryState},
6    system::{
7        DynSystemParam, DynSystemParamState, Local, ParamSet, Query, SystemMeta, SystemParam,
8    },
9    world::{
10        FilteredResources, FilteredResourcesBuilder, FilteredResourcesMut,
11        FilteredResourcesMutBuilder, FromWorld, World,
12    },
13};
14use core::fmt::Debug;
15
16use super::{init_query_param, Res, ResMut, Resource, SystemState};
17
18/// A builder that can create a [`SystemParam`].
19///
20/// ```
21/// # use bevy_ecs::{
22/// #     prelude::*,
23/// #     system::{SystemParam, ParamBuilder},
24/// # };
25/// # #[derive(Resource)]
26/// # struct R;
27/// #
28/// # #[derive(SystemParam)]
29/// # struct MyParam;
30/// #
31/// fn some_system(param: MyParam) {}
32///
33/// fn build_system(builder: impl SystemParamBuilder<MyParam>) {
34///     let mut world = World::new();
35///     // To build a system, create a tuple of `SystemParamBuilder`s
36///     // with a builder for each parameter.
37///     // Note that the builder for a system must be a tuple,
38///     // even if there is only one parameter.
39///     (builder,)
40///         .build_state(&mut world)
41///         .build_system(some_system);
42/// }
43///
44/// fn build_closure_system_infer(builder: impl SystemParamBuilder<MyParam>) {
45///     let mut world = World::new();
46///     // Closures can be used in addition to named functions.
47///     // If a closure is used, the parameter types must all be inferred
48///     // from the builders, so you cannot use plain `ParamBuilder`.
49///     (builder, ParamBuilder::resource())
50///         .build_state(&mut world)
51///         .build_system(|param, res| {
52///             let param: MyParam = param;
53///             let res: Res<R> = res;
54///         });
55/// }
56///
57/// fn build_closure_system_explicit(builder: impl SystemParamBuilder<MyParam>) {
58///     let mut world = World::new();
59///     // Alternately, you can provide all types in the closure
60///     // parameter list and call `build_any_system()`.
61///     (builder, ParamBuilder)
62///         .build_state(&mut world)
63///         .build_any_system(|param: MyParam, res: Res<R>| {});
64/// }
65/// ```
66///
67/// See the documentation for individual builders for more examples.
68///
69/// # List of Builders
70///
71/// [`ParamBuilder`] can be used for parameters that don't require any special building.
72/// Using a `ParamBuilder` will build the system parameter the same way it would be initialized in an ordinary system.
73///
74/// `ParamBuilder` also provides factory methods that return a `ParamBuilder` typed as `impl SystemParamBuilder<P>`
75/// for common system parameters that can be used to guide closure parameter inference.
76///
77/// [`QueryParamBuilder`] can build a [`Query`] to add additional filters,
78/// or to configure the components available to [`FilteredEntityRef`](crate::world::FilteredEntityRef) or [`FilteredEntityMut`](crate::world::FilteredEntityMut).
79/// You can also use a [`QueryState`] to build a [`Query`].
80///
81/// [`LocalBuilder`] can build a [`Local`] to supply the initial value for the `Local`.
82///
83/// [`FilteredResourcesParamBuilder`] can build a [`FilteredResources`],
84/// and [`FilteredResourcesMutParamBuilder`] can build a [`FilteredResourcesMut`],
85/// to configure the resources that can be accessed.
86///
87/// [`DynParamBuilder`] can build a [`DynSystemParam`] to determine the type of the inner parameter,
88/// and to supply any `SystemParamBuilder` it needs.
89///
90/// Tuples of builders can build tuples of parameters, one builder for each element.
91/// Note that since systems require a tuple as a parameter, the outer builder for a system will always be a tuple.
92///
93/// A [`Vec`] of builders can build a `Vec` of parameters, one builder for each element.
94///
95/// A [`ParamSetBuilder`] can build a [`ParamSet`].
96/// This can wrap either a tuple or a `Vec`, one builder for each element.
97///
98/// A custom system param created with `#[derive(SystemParam)]` can be buildable if it includes a `#[system_param(builder)]` attribute.
99/// See [the documentation for `SystemParam` derives](SystemParam#builders).
100///
101/// # Safety
102///
103/// The implementor must ensure the following is true.
104/// - [`SystemParamBuilder::build`] correctly registers all [`World`] accesses used
105///   by [`SystemParam::get_param`] with the provided [`system_meta`](SystemMeta).
106/// - None of the world accesses may conflict with any prior accesses registered
107///   on `system_meta`.
108///
109/// Note that this depends on the implementation of [`SystemParam::get_param`],
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, meta: &mut SystemMeta) -> 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, Copy, 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, meta: &mut SystemMeta) -> P::State {
169        P::init_state(world, meta)
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: Calls `init_query_param`, just like `Query::init_state`.
208unsafe impl<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>
209    SystemParamBuilder<Query<'w, 's, D, F>> for QueryState<D, F>
210{
211    fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> QueryState<D, F> {
212        self.validate_world(world.id());
213        init_query_param(world, system_meta, &self);
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 an `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/// ```
258pub struct QueryParamBuilder<T>(T);
259
260impl<T> QueryParamBuilder<T> {
261    /// Creates a [`SystemParamBuilder`] for a [`Query`] that accepts a callback to configure the [`QueryBuilder`].
262    pub fn new<D: QueryData, F: QueryFilter>(f: T) -> Self
263    where
264        T: FnOnce(&mut QueryBuilder<D, F>),
265    {
266        Self(f)
267    }
268}
269
270impl<'a, D: QueryData, F: QueryFilter>
271    QueryParamBuilder<Box<dyn FnOnce(&mut QueryBuilder<D, F>) + 'a>>
272{
273    /// Creates a [`SystemParamBuilder`] for a [`Query`] that accepts a callback to configure the [`QueryBuilder`].
274    /// This boxes the callback so that it has a common type and can be put in a `Vec`.
275    pub fn new_box(f: impl FnOnce(&mut QueryBuilder<D, F>) + 'a) -> Self {
276        Self(Box::new(f))
277    }
278}
279
280// SAFETY: Calls `init_query_param`, just like `Query::init_state`.
281unsafe impl<
282        'w,
283        's,
284        D: QueryData + 'static,
285        F: QueryFilter + 'static,
286        T: FnOnce(&mut QueryBuilder<D, F>),
287    > SystemParamBuilder<Query<'w, 's, D, F>> for QueryParamBuilder<T>
288{
289    fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> QueryState<D, F> {
290        let mut builder = QueryBuilder::new(world);
291        (self.0)(&mut builder);
292        let state = builder.build();
293        init_query_param(world, system_meta, &state);
294        state
295    }
296}
297
298macro_rules! impl_system_param_builder_tuple {
299    ($(#[$meta:meta])* $(($param: ident, $builder: ident)),*) => {
300        $(#[$meta])*
301        // SAFETY: implementors of each `SystemParamBuilder` in the tuple have validated their impls
302        unsafe impl<$($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<($($param,)*)> for ($($builder,)*) {
303            fn build(self, _world: &mut World, _meta: &mut SystemMeta) -> <($($param,)*) as SystemParam>::State {
304                #[allow(non_snake_case)]
305                let ($($builder,)*) = self;
306                #[allow(clippy::unused_unit)]
307                ($($builder.build(_world, _meta),)*)
308            }
309        }
310    };
311}
312
313all_tuples!(
314    #[doc(fake_variadic)]
315    impl_system_param_builder_tuple,
316    0,
317    16,
318    P,
319    B
320);
321
322// SAFETY: implementors of each `SystemParamBuilder` in the vec have validated their impls
323unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<Vec<P>> for Vec<B> {
324    fn build(self, world: &mut World, meta: &mut SystemMeta) -> <Vec<P> as SystemParam>::State {
325        self.into_iter()
326            .map(|builder| builder.build(world, meta))
327            .collect()
328    }
329}
330
331/// A [`SystemParamBuilder`] for a [`ParamSet`].
332///
333/// To build a [`ParamSet`] with a tuple of system parameters, pass a tuple of matching [`SystemParamBuilder`]s.
334/// To build a [`ParamSet`] with a [`Vec`] of system parameters, pass a `Vec` of matching [`SystemParamBuilder`]s.
335///
336/// # Examples
337///
338/// ```
339/// # use bevy_ecs::{prelude::*, system::*};
340/// #
341/// # #[derive(Component)]
342/// # struct Health;
343/// #
344/// # #[derive(Component)]
345/// # struct Enemy;
346/// #
347/// # #[derive(Component)]
348/// # struct Ally;
349/// #
350/// # let mut world = World::new();
351/// #
352/// let system = (ParamSetBuilder((
353///     QueryParamBuilder::new(|builder| {
354///         builder.with::<Enemy>();
355///     }),
356///     QueryParamBuilder::new(|builder| {
357///         builder.with::<Ally>();
358///     }),
359///     ParamBuilder,
360/// )),)
361///     .build_state(&mut world)
362///     .build_system(buildable_system_with_tuple);
363/// # world.run_system_once(system);
364///
365/// fn buildable_system_with_tuple(
366///     mut set: ParamSet<(Query<&mut Health>, Query<&mut Health>, &World)>,
367/// ) {
368///     // The first parameter is built from the first builder,
369///     // so this will iterate over enemies.
370///     for mut health in set.p0().iter_mut() {}
371///     // And the second parameter is built from the second builder,
372///     // so this will iterate over allies.
373///     for mut health in set.p1().iter_mut() {}
374///     // Parameters that don't need special building can use `ParamBuilder`.
375///     let entities = set.p2().entities();
376/// }
377///
378/// let system = (ParamSetBuilder(vec![
379///     QueryParamBuilder::new_box(|builder| {
380///         builder.with::<Enemy>();
381///     }),
382///     QueryParamBuilder::new_box(|builder| {
383///         builder.with::<Ally>();
384///     }),
385/// ]),)
386///     .build_state(&mut world)
387///     .build_system(buildable_system_with_vec);
388/// # world.run_system_once(system);
389///
390/// fn buildable_system_with_vec(mut set: ParamSet<Vec<Query<&mut Health>>>) {
391///     // As with tuples, the first parameter is built from the first builder,
392///     // so this will iterate over enemies.
393///     for mut health in set.get_mut(0).iter_mut() {}
394///     // And the second parameter is built from the second builder,
395///     // so this will iterate over allies.
396///     for mut health in set.get_mut(1).iter_mut() {}
397///     // You can iterate over the parameters either by index,
398///     // or using the `for_each` method.
399///     set.for_each(|mut query| for mut health in query.iter_mut() {});
400/// }
401/// ```
402pub struct ParamSetBuilder<T>(pub T);
403
404macro_rules! impl_param_set_builder_tuple {
405    ($(($param: ident, $builder: ident, $meta: ident)),*) => {
406        // SAFETY: implementors of each `SystemParamBuilder` in the tuple have validated their impls
407        unsafe impl<'w, 's, $($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<ParamSet<'w, 's, ($($param,)*)>> for ParamSetBuilder<($($builder,)*)> {
408            #[allow(non_snake_case)]
409            fn build(self, _world: &mut World, _system_meta: &mut SystemMeta) -> <($($param,)*) as SystemParam>::State {
410                let ParamSetBuilder(($($builder,)*)) = self;
411                // Note that this is slightly different from `init_state`, which calls `init_state` on each param twice.
412                // One call populates an empty `SystemMeta` with the new access, while the other runs against a cloned `SystemMeta` to check for conflicts.
413                // Builders can only be invoked once, so we do both in a single call here.
414                // That means that any `filtered_accesses` in the `component_access_set` will get copied to every `$meta`
415                // and will appear multiple times in the final `SystemMeta`.
416                $(
417                    let mut $meta = _system_meta.clone();
418                    let $param = $builder.build(_world, &mut $meta);
419                )*
420                // Make the ParamSet non-send if any of its parameters are non-send.
421                if false $(|| !$meta.is_send())* {
422                    _system_meta.set_non_send();
423                }
424                $(
425                    _system_meta
426                        .component_access_set
427                        .extend($meta.component_access_set);
428                    _system_meta
429                        .archetype_component_access
430                        .extend(&$meta.archetype_component_access);
431                )*
432                #[allow(clippy::unused_unit)]
433                ($($param,)*)
434            }
435        }
436    };
437}
438
439all_tuples!(impl_param_set_builder_tuple, 1, 8, P, B, meta);
440
441// SAFETY: Relevant parameter ComponentId and ArchetypeComponentId access is applied to SystemMeta. If any ParamState conflicts
442// with any prior access, a panic will occur.
443unsafe impl<'w, 's, P: SystemParam, B: SystemParamBuilder<P>>
444    SystemParamBuilder<ParamSet<'w, 's, Vec<P>>> for ParamSetBuilder<Vec<B>>
445{
446    fn build(
447        self,
448        world: &mut World,
449        system_meta: &mut SystemMeta,
450    ) -> <Vec<P> as SystemParam>::State {
451        let mut states = Vec::with_capacity(self.0.len());
452        let mut metas = Vec::with_capacity(self.0.len());
453        for builder in self.0 {
454            let mut meta = system_meta.clone();
455            states.push(builder.build(world, &mut meta));
456            metas.push(meta);
457        }
458        if metas.iter().any(|m| !m.is_send()) {
459            system_meta.set_non_send();
460        }
461        for meta in metas {
462            system_meta
463                .component_access_set
464                .extend(meta.component_access_set);
465            system_meta
466                .archetype_component_access
467                .extend(&meta.archetype_component_access);
468        }
469        states
470    }
471}
472
473/// A [`SystemParamBuilder`] for a [`DynSystemParam`].
474/// See the [`DynSystemParam`] docs for examples.
475pub struct DynParamBuilder<'a>(
476    Box<dyn FnOnce(&mut World, &mut SystemMeta) -> DynSystemParamState + 'a>,
477);
478
479impl<'a> DynParamBuilder<'a> {
480    /// Creates a new [`DynParamBuilder`] by wrapping a [`SystemParamBuilder`] of any type.
481    /// The built [`DynSystemParam`] can be downcast to `T`.
482    pub fn new<T: SystemParam + 'static>(builder: impl SystemParamBuilder<T> + 'a) -> Self {
483        Self(Box::new(|world, meta| {
484            DynSystemParamState::new::<T>(builder.build(world, meta))
485        }))
486    }
487}
488
489// SAFETY: `DynSystemParam::get_param` will call `get_param` on the boxed `DynSystemParamState`,
490// and the boxed builder was a valid implementation of `SystemParamBuilder` for that type.
491// The resulting `DynSystemParam` can only perform access by downcasting to that param type.
492unsafe impl<'a, 'w, 's> SystemParamBuilder<DynSystemParam<'w, 's>> for DynParamBuilder<'a> {
493    fn build(
494        self,
495        world: &mut World,
496        meta: &mut SystemMeta,
497    ) -> <DynSystemParam<'w, 's> as SystemParam>::State {
498        (self.0)(world, meta)
499    }
500}
501
502/// A [`SystemParamBuilder`] for a [`Local`].
503/// The provided value will be used as the initial value of the `Local`.
504///
505/// ## Example
506///
507/// ```
508/// # use bevy_ecs::{
509/// #     prelude::*,
510/// #     system::{SystemParam, LocalBuilder, RunSystemOnce},
511/// # };
512/// #
513/// # let mut world = World::new();
514/// let system = (LocalBuilder(100),)
515///     .build_state(&mut world)
516///     .build_system(|local: Local<usize>| {
517///         assert_eq!(*local, 100);
518///     });
519/// # world.run_system_once(system);
520/// ```
521pub struct LocalBuilder<T>(pub T);
522
523// SAFETY: `Local` performs no world access.
524unsafe impl<'s, T: FromWorld + Send + 'static> SystemParamBuilder<Local<'s, T>>
525    for LocalBuilder<T>
526{
527    fn build(
528        self,
529        _world: &mut World,
530        _meta: &mut SystemMeta,
531    ) -> <Local<'s, T> as SystemParam>::State {
532        SyncCell::new(self.0)
533    }
534}
535
536/// A [`SystemParamBuilder`] for a [`FilteredResources`].
537/// See the [`FilteredResources`] docs for examples.
538pub struct FilteredResourcesParamBuilder<T>(T);
539
540impl<T> FilteredResourcesParamBuilder<T> {
541    /// Creates a [`SystemParamBuilder`] for a [`FilteredResources`] that accepts a callback to configure the [`FilteredResourcesBuilder`].
542    pub fn new(f: T) -> Self
543    where
544        T: FnOnce(&mut FilteredResourcesBuilder),
545    {
546        Self(f)
547    }
548}
549
550impl<'a> FilteredResourcesParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesBuilder) + 'a>> {
551    /// Creates a [`SystemParamBuilder`] for a [`FilteredResources`] that accepts a callback to configure the [`FilteredResourcesBuilder`].
552    /// This boxes the callback so that it has a common type.
553    pub fn new_box(f: impl FnOnce(&mut FilteredResourcesBuilder) + 'a) -> Self {
554        Self(Box::new(f))
555    }
556}
557
558// SAFETY: Resource ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this FilteredResources
559// conflicts with any prior access, a panic will occur.
560unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesBuilder)>
561    SystemParamBuilder<FilteredResources<'w, 's>> for FilteredResourcesParamBuilder<T>
562{
563    fn build(
564        self,
565        world: &mut World,
566        meta: &mut SystemMeta,
567    ) -> <FilteredResources<'w, 's> as SystemParam>::State {
568        let mut builder = FilteredResourcesBuilder::new(world);
569        (self.0)(&mut builder);
570        let access = builder.build();
571
572        let combined_access = meta.component_access_set.combined_access();
573        let conflicts = combined_access.get_conflicts(&access);
574        if !conflicts.is_empty() {
575            let accesses = conflicts.format_conflict_list(world);
576            let system_name = &meta.name;
577            panic!("error[B0002]: FilteredResources in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002");
578        }
579
580        if access.has_read_all_resources() {
581            meta.component_access_set
582                .add_unfiltered_read_all_resources();
583            meta.archetype_component_access.read_all_resources();
584        } else {
585            for component_id in access.resource_reads_and_writes() {
586                meta.component_access_set
587                    .add_unfiltered_resource_read(component_id);
588
589                let archetype_component_id = world.initialize_resource_internal(component_id).id();
590                meta.archetype_component_access
591                    .add_resource_read(archetype_component_id);
592            }
593        }
594
595        access
596    }
597}
598
599/// A [`SystemParamBuilder`] for a [`FilteredResourcesMut`].
600/// See the [`FilteredResourcesMut`] docs for examples.
601pub struct FilteredResourcesMutParamBuilder<T>(T);
602
603impl<T> FilteredResourcesMutParamBuilder<T> {
604    /// Creates a [`SystemParamBuilder`] for a [`FilteredResourcesMut`] that accepts a callback to configure the [`FilteredResourcesMutBuilder`].
605    pub fn new(f: T) -> Self
606    where
607        T: FnOnce(&mut FilteredResourcesMutBuilder),
608    {
609        Self(f)
610    }
611}
612
613impl<'a> FilteredResourcesMutParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesMutBuilder) + 'a>> {
614    /// Creates a [`SystemParamBuilder`] for a [`FilteredResourcesMut`] that accepts a callback to configure the [`FilteredResourcesMutBuilder`].
615    /// This boxes the callback so that it has a common type.
616    pub fn new_box(f: impl FnOnce(&mut FilteredResourcesMutBuilder) + 'a) -> Self {
617        Self(Box::new(f))
618    }
619}
620
621// SAFETY: Resource ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this FilteredResources
622// conflicts with any prior access, a panic will occur.
623unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesMutBuilder)>
624    SystemParamBuilder<FilteredResourcesMut<'w, 's>> for FilteredResourcesMutParamBuilder<T>
625{
626    fn build(
627        self,
628        world: &mut World,
629        meta: &mut SystemMeta,
630    ) -> <FilteredResourcesMut<'w, 's> as SystemParam>::State {
631        let mut builder = FilteredResourcesMutBuilder::new(world);
632        (self.0)(&mut builder);
633        let access = builder.build();
634
635        let combined_access = meta.component_access_set.combined_access();
636        let conflicts = combined_access.get_conflicts(&access);
637        if !conflicts.is_empty() {
638            let accesses = conflicts.format_conflict_list(world);
639            let system_name = &meta.name;
640            panic!("error[B0002]: FilteredResourcesMut in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002");
641        }
642
643        if access.has_read_all_resources() {
644            meta.component_access_set
645                .add_unfiltered_read_all_resources();
646            meta.archetype_component_access.read_all_resources();
647        } else {
648            for component_id in access.resource_reads() {
649                meta.component_access_set
650                    .add_unfiltered_resource_read(component_id);
651
652                let archetype_component_id = world.initialize_resource_internal(component_id).id();
653                meta.archetype_component_access
654                    .add_resource_read(archetype_component_id);
655            }
656        }
657
658        if access.has_write_all_resources() {
659            meta.component_access_set
660                .add_unfiltered_write_all_resources();
661            meta.archetype_component_access.write_all_resources();
662        } else {
663            for component_id in access.resource_writes() {
664                meta.component_access_set
665                    .add_unfiltered_resource_write(component_id);
666
667                let archetype_component_id = world.initialize_resource_internal(component_id).id();
668                meta.archetype_component_access
669                    .add_resource_write(archetype_component_id);
670            }
671        }
672
673        access
674    }
675}
676
677#[cfg(test)]
678mod tests {
679    use crate as bevy_ecs;
680    use crate::{
681        entity::Entities,
682        prelude::{Component, Query},
683        system::{Local, RunSystemOnce},
684    };
685
686    use super::*;
687
688    #[derive(Component)]
689    struct A;
690
691    #[derive(Component)]
692    struct B;
693
694    #[derive(Component)]
695    struct C;
696
697    #[derive(Resource, Default)]
698    struct R;
699
700    fn local_system(local: Local<u64>) -> u64 {
701        *local
702    }
703
704    fn query_system(query: Query<()>) -> usize {
705        query.iter().count()
706    }
707
708    fn multi_param_system(a: Local<u64>, b: Local<u64>) -> u64 {
709        *a + *b + 1
710    }
711
712    #[test]
713    fn local_builder() {
714        let mut world = World::new();
715
716        let system = (LocalBuilder(10),)
717            .build_state(&mut world)
718            .build_system(local_system);
719
720        let output = world.run_system_once(system).unwrap();
721        assert_eq!(output, 10);
722    }
723
724    #[test]
725    fn query_builder() {
726        let mut world = World::new();
727
728        world.spawn(A);
729        world.spawn_empty();
730
731        let system = (QueryParamBuilder::new(|query| {
732            query.with::<A>();
733        }),)
734            .build_state(&mut world)
735            .build_system(query_system);
736
737        let output = world.run_system_once(system).unwrap();
738        assert_eq!(output, 1);
739    }
740
741    #[test]
742    fn query_builder_state() {
743        let mut world = World::new();
744
745        world.spawn(A);
746        world.spawn_empty();
747
748        let state = QueryBuilder::new(&mut world).with::<A>().build();
749
750        let system = (state,).build_state(&mut world).build_system(query_system);
751
752        let output = world.run_system_once(system).unwrap();
753        assert_eq!(output, 1);
754    }
755
756    #[test]
757    fn multi_param_builder() {
758        let mut world = World::new();
759
760        world.spawn(A);
761        world.spawn_empty();
762
763        let system = (LocalBuilder(0), ParamBuilder)
764            .build_state(&mut world)
765            .build_system(multi_param_system);
766
767        let output = world.run_system_once(system).unwrap();
768        assert_eq!(output, 1);
769    }
770
771    #[test]
772    fn vec_builder() {
773        let mut world = World::new();
774
775        world.spawn((A, B, C));
776        world.spawn((A, B));
777        world.spawn((A, C));
778        world.spawn((A, C));
779        world.spawn_empty();
780
781        let system = (vec![
782            QueryParamBuilder::new_box(|builder| {
783                builder.with::<B>().without::<C>();
784            }),
785            QueryParamBuilder::new_box(|builder| {
786                builder.with::<C>().without::<B>();
787            }),
788        ],)
789            .build_state(&mut world)
790            .build_system(|params: Vec<Query<&mut A>>| {
791                let mut count: usize = 0;
792                params
793                    .into_iter()
794                    .for_each(|mut query| count += query.iter_mut().count());
795                count
796            });
797
798        let output = world.run_system_once(system).unwrap();
799        assert_eq!(output, 3);
800    }
801
802    #[test]
803    fn multi_param_builder_inference() {
804        let mut world = World::new();
805
806        world.spawn(A);
807        world.spawn_empty();
808
809        let system = (LocalBuilder(0u64), ParamBuilder::local::<u64>())
810            .build_state(&mut world)
811            .build_system(|a, b| *a + *b + 1);
812
813        let output = world.run_system_once(system).unwrap();
814        assert_eq!(output, 1);
815    }
816
817    #[test]
818    fn param_set_builder() {
819        let mut world = World::new();
820
821        world.spawn((A, B, C));
822        world.spawn((A, B));
823        world.spawn((A, C));
824        world.spawn((A, C));
825        world.spawn_empty();
826
827        let system = (ParamSetBuilder((
828            QueryParamBuilder::new(|builder| {
829                builder.with::<B>();
830            }),
831            QueryParamBuilder::new(|builder| {
832                builder.with::<C>();
833            }),
834        )),)
835            .build_state(&mut world)
836            .build_system(|mut params: ParamSet<(Query<&mut A>, Query<&mut A>)>| {
837                params.p0().iter().count() + params.p1().iter().count()
838            });
839
840        let output = world.run_system_once(system).unwrap();
841        assert_eq!(output, 5);
842    }
843
844    #[test]
845    fn param_set_vec_builder() {
846        let mut world = World::new();
847
848        world.spawn((A, B, C));
849        world.spawn((A, B));
850        world.spawn((A, C));
851        world.spawn((A, C));
852        world.spawn_empty();
853
854        let system = (ParamSetBuilder(vec![
855            QueryParamBuilder::new_box(|builder| {
856                builder.with::<B>();
857            }),
858            QueryParamBuilder::new_box(|builder| {
859                builder.with::<C>();
860            }),
861        ]),)
862            .build_state(&mut world)
863            .build_system(|mut params: ParamSet<Vec<Query<&mut A>>>| {
864                let mut count = 0;
865                params.for_each(|mut query| count += query.iter_mut().count());
866                count
867            });
868
869        let output = world.run_system_once(system).unwrap();
870        assert_eq!(output, 5);
871    }
872
873    #[test]
874    fn dyn_builder() {
875        let mut world = World::new();
876
877        world.spawn(A);
878        world.spawn_empty();
879
880        let system = (
881            DynParamBuilder::new(LocalBuilder(3_usize)),
882            DynParamBuilder::new::<Query<()>>(QueryParamBuilder::new(|builder| {
883                builder.with::<A>();
884            })),
885            DynParamBuilder::new::<&Entities>(ParamBuilder),
886        )
887            .build_state(&mut world)
888            .build_system(
889                |mut p0: DynSystemParam, mut p1: DynSystemParam, mut p2: DynSystemParam| {
890                    let local = *p0.downcast_mut::<Local<usize>>().unwrap();
891                    let query_count = p1.downcast_mut::<Query<()>>().unwrap().iter().count();
892                    let _entities = p2.downcast_mut::<&Entities>().unwrap();
893                    assert!(p0.downcast_mut::<Query<()>>().is_none());
894                    local + query_count
895                },
896            );
897
898        let output = world.run_system_once(system).unwrap();
899        assert_eq!(output, 4);
900    }
901
902    #[derive(SystemParam)]
903    #[system_param(builder)]
904    struct CustomParam<'w, 's> {
905        query: Query<'w, 's, ()>,
906        local: Local<'s, usize>,
907    }
908
909    #[test]
910    fn custom_param_builder() {
911        let mut world = World::new();
912
913        world.spawn(A);
914        world.spawn_empty();
915
916        let system = (CustomParamBuilder {
917            local: LocalBuilder(100),
918            query: QueryParamBuilder::new(|builder| {
919                builder.with::<A>();
920            }),
921        },)
922            .build_state(&mut world)
923            .build_system(|param: CustomParam| *param.local + param.query.iter().count());
924
925        let output = world.run_system_once(system).unwrap();
926        assert_eq!(output, 101);
927    }
928
929    #[test]
930    fn filtered_resource_conflicts_read_with_res() {
931        let mut world = World::new();
932        (
933            ParamBuilder::resource(),
934            FilteredResourcesParamBuilder::new(|builder| {
935                builder.add_read::<R>();
936            }),
937        )
938            .build_state(&mut world)
939            .build_system(|_r: Res<R>, _fr: FilteredResources| {});
940    }
941
942    #[test]
943    #[should_panic]
944    fn filtered_resource_conflicts_read_with_resmut() {
945        let mut world = World::new();
946        (
947            ParamBuilder::resource_mut(),
948            FilteredResourcesParamBuilder::new(|builder| {
949                builder.add_read::<R>();
950            }),
951        )
952            .build_state(&mut world)
953            .build_system(|_r: ResMut<R>, _fr: FilteredResources| {});
954    }
955
956    #[test]
957    #[should_panic]
958    fn filtered_resource_conflicts_read_all_with_resmut() {
959        let mut world = World::new();
960        (
961            ParamBuilder::resource_mut(),
962            FilteredResourcesParamBuilder::new(|builder| {
963                builder.add_read_all();
964            }),
965        )
966            .build_state(&mut world)
967            .build_system(|_r: ResMut<R>, _fr: FilteredResources| {});
968    }
969
970    #[test]
971    fn filtered_resource_mut_conflicts_read_with_res() {
972        let mut world = World::new();
973        (
974            ParamBuilder::resource(),
975            FilteredResourcesMutParamBuilder::new(|builder| {
976                builder.add_read::<R>();
977            }),
978        )
979            .build_state(&mut world)
980            .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
981    }
982
983    #[test]
984    #[should_panic]
985    fn filtered_resource_mut_conflicts_read_with_resmut() {
986        let mut world = World::new();
987        (
988            ParamBuilder::resource_mut(),
989            FilteredResourcesMutParamBuilder::new(|builder| {
990                builder.add_read::<R>();
991            }),
992        )
993            .build_state(&mut world)
994            .build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
995    }
996
997    #[test]
998    #[should_panic]
999    fn filtered_resource_mut_conflicts_write_with_res() {
1000        let mut world = World::new();
1001        (
1002            ParamBuilder::resource(),
1003            FilteredResourcesMutParamBuilder::new(|builder| {
1004                builder.add_write::<R>();
1005            }),
1006        )
1007            .build_state(&mut world)
1008            .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1009    }
1010
1011    #[test]
1012    #[should_panic]
1013    fn filtered_resource_mut_conflicts_write_all_with_res() {
1014        let mut world = World::new();
1015        (
1016            ParamBuilder::resource(),
1017            FilteredResourcesMutParamBuilder::new(|builder| {
1018                builder.add_write_all();
1019            }),
1020        )
1021            .build_state(&mut world)
1022            .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1023    }
1024
1025    #[test]
1026    #[should_panic]
1027    fn filtered_resource_mut_conflicts_write_with_resmut() {
1028        let mut world = World::new();
1029        (
1030            ParamBuilder::resource_mut(),
1031            FilteredResourcesMutParamBuilder::new(|builder| {
1032                builder.add_write::<R>();
1033            }),
1034        )
1035            .build_state(&mut world)
1036            .build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
1037    }
1038}