Skip to main content

bevy_ecs/system/
builder.rs

1use alloc::{boxed::Box, vec::Vec};
2use bevy_platform::cell::SyncCell;
3use bevy_utils::prelude::DebugName;
4use smallvec::SmallVec;
5use variadics_please::all_tuples;
6
7use crate::{
8    change_detection::{CheckChangeTicks, Tick},
9    component::Mutable,
10    prelude::QueryBuilder,
11    query::{QueryData, QueryFilter, QueryState},
12    resource::Resource,
13    system::{
14        DynSystemParam, DynSystemParamState, FromInput, FunctionSystem, If, IntoResult, IntoSystem,
15        Local, ParamSet, Query, ReadOnlySystem, System, SystemAccess, SystemInput, SystemMeta,
16        SystemParam, SystemParamFunction, SystemParamValidationError,
17    },
18    world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, FromWorld, World},
19};
20
21#[expect(deprecated, reason = "`FilteredResources` will be removed.")]
22use crate::world::{
23    FilteredResources, FilteredResourcesBuilder, FilteredResourcesMut, FilteredResourcesMutBuilder,
24};
25
26use core::{fmt::Debug, marker::PhantomData, mem};
27
28use super::{Res, ResMut, RunSystemError, SystemState, SystemStateFlags};
29
30/// A builder that can create a [`SystemParam`].
31///
32/// ```
33/// # use bevy_ecs::{
34/// #     prelude::*,
35/// #     system::{SystemParam, ParamBuilder},
36/// # };
37/// # #[derive(Resource)]
38/// # struct R;
39/// #
40/// # #[derive(SystemParam)]
41/// # struct MyParam;
42/// #
43/// fn some_system(param: MyParam) {}
44///
45/// fn build_system(builder: impl SystemParamBuilder<MyParam> + 'static) {
46///     // To build a system, create a tuple of `SystemParamBuilder`s
47///     // with a builder for each parameter.
48///     // Note that the builder for a system must be a tuple,
49///     // even if there is only one parameter.
50/// #   let _system: bevy_ecs::system::IntoBuilderSystem<fn(MyParam), (), (), _, _> =
51///     (builder,)
52///         .build_system(some_system);
53/// }
54///
55/// fn build_system_direct(builder: impl SystemParamBuilder<MyParam>) {
56///     let mut world = World::new();
57///     // You can also construct a system in two steps, first by
58///     // constructing a [`SystemState`] with `build_state` and
59///     // second by constructing the final system with `build_system`.
60///     // This can be useful in cases that require type inference
61///     // for function parameters (like closures!), since normal
62///     // `build_system` requires explicitly specifying all parameter
63///     // types. See `build_closure_system_infer/explicit` below for more
64///     // info.
65///     (builder,)
66///         .build_state(&mut world)
67///         .build_system(some_system);
68/// }
69///
70/// fn build_closure_system_infer(builder: impl SystemParamBuilder<MyParam>) {
71///     let mut world = World::new();
72///     // Closures can be used in addition to named functions.
73///     // If a closure is used, the parameter types must all be inferred
74///     // from the builders, so you cannot use plain `ParamBuilder`.
75///     (builder, ParamBuilder::resource())
76///         .build_state(&mut world)
77///         .build_system(|param, res| {
78///             let param: MyParam = param;
79///             let res: Res<R> = res;
80///         });
81/// }
82///
83/// fn build_closure_system_explicit(builder: impl SystemParamBuilder<MyParam>) {
84///     let mut world = World::new();
85///     // Alternately, you can provide all types in the closure
86///     // parameter list and call `build_system()` normally.
87///     (builder, ParamBuilder::resource())
88///         .build_state(&mut world) // this line can be optionally omitted, since all the parameter types are explicit!
89///         .build_system(|param: MyParam, res: Res<R>| {});
90/// }
91/// ```
92///
93/// See the documentation for individual builders for more examples.
94///
95/// # List of Builders
96///
97/// [`ParamBuilder`] can be used for parameters that don't require any special building.
98/// Using a `ParamBuilder` will build the system parameter the same way it would be initialized in an ordinary system.
99///
100/// `ParamBuilder` also provides factory methods that return a `ParamBuilder` typed as `impl SystemParamBuilder<P>`
101/// for common system parameters that can be used to guide closure parameter inference.
102///
103/// [`QueryParamBuilder`] can build a [`Query`] to add additional filters,
104/// or to configure the components available to [`FilteredEntityRef`](crate::world::FilteredEntityRef) or [`FilteredEntityMut`](crate::world::FilteredEntityMut).
105/// You can also use a [`QueryState`] to build a [`Query`].
106///
107/// [`LocalBuilder`] can build a [`Local`] to supply the initial value for the `Local`.
108///
109/// [`FilteredResourcesParamBuilder`] can build a [`FilteredResources`],
110/// and [`FilteredResourcesMutParamBuilder`] can build a [`FilteredResourcesMut`],
111/// to configure the resources that can be accessed.
112///
113/// [`DynParamBuilder`] can build a [`DynSystemParam`] to determine the type of the inner parameter,
114/// and to supply any `SystemParamBuilder` it needs.
115///
116/// Tuples of builders can build tuples of parameters, one builder for each element.
117/// Note that since systems require a tuple as a parameter, the outer builder for a system will always be a tuple.
118///
119/// A [`Vec`] of builders can build a `Vec` of parameters, one builder for each element.
120///
121/// A [`ParamSetBuilder`] can build a [`ParamSet`].
122/// This can wrap either a tuple or a `Vec`, one builder for each element.
123///
124/// A custom system param created with `#[derive(SystemParam)]` can be buildable if it includes a `#[system_param(builder)]` attribute.
125/// See [the documentation for `SystemParam` derives](SystemParam#builders).
126///
127/// # Safety
128///
129/// The implementor must ensure that the state returned
130/// from [`SystemParamBuilder::build`] is valid for `P`.
131/// Note that the exact safety requirements depend on the implementation of [`SystemParam`],
132/// so if `Self` is not a local type then you must call [`SystemParam::init_state`]
133/// or another [`SystemParamBuilder::build`].
134pub unsafe trait SystemParamBuilder<P: SystemParam>: Sized {
135    /// Registers any [`World`] access used by this [`SystemParam`]
136    /// and creates a new instance of this param's [`State`](SystemParam::State).
137    fn build(self, world: &mut World) -> P::State;
138
139    /// Create a [`SystemState`] from a [`SystemParamBuilder`].
140    /// To create a system, call [`SystemState::build_system`] on the result.
141    fn build_state(self, world: &mut World) -> SystemState<P> {
142        SystemState::from_builder(world, self)
143    }
144
145    /// Create a [`System`] from a [`SystemParamBuilder`] directly.
146    ///
147    /// This method is useful in cases where type inference for
148    /// closure parameters isn't necessary, or where it's not
149    /// possible to call [`SystemState::build_system`] by passing
150    /// in an `&mut World`. Rather than constructing the system's
151    /// state immediately, this function returns a wrapper that
152    /// initializes the system state during the first run.
153    ///
154    /// Caveats:
155    /// - doesn't support parameter type inference.
156    /// - only works for 'static system param builder types.
157    ///
158    /// In cases where  either of these are required, call
159    /// [`SystemParamBuilder::build_state`] instead.
160    fn build_system<Marker, In, Out, Func>(
161        self,
162        func: Func,
163    ) -> IntoBuilderSystem<Marker, In, Out, Func, Self>
164    where
165        Self: 'static,
166        Func: SystemParamFunction<Marker, Param = P>,
167    {
168        IntoBuilderSystem::new(self, func)
169    }
170}
171
172/// A [`SystemParamBuilder`] for any [`SystemParam`] that uses its default initialization.
173///
174/// ## Example
175///
176/// ```
177/// # use bevy_ecs::{
178/// #     prelude::*,
179/// #     system::{SystemParam, ParamBuilder},
180/// # };
181/// #
182/// # #[derive(Component)]
183/// # struct A;
184/// #
185/// # #[derive(Resource)]
186/// # struct R;
187/// #
188/// # #[derive(SystemParam)]
189/// # struct MyParam;
190/// #
191/// # let mut world = World::new();
192/// # world.insert_resource(R);
193/// #
194/// fn my_system(res: Res<R>, param: MyParam, query: Query<&A>) {
195///     // ...
196/// }
197///
198/// let system = (
199///     // A plain ParamBuilder can build any parameter type.
200///     ParamBuilder,
201///     // The `of::<P>()` method returns a `ParamBuilder`
202///     // typed as `impl SystemParamBuilder<P>`.
203///     ParamBuilder::of::<MyParam>(),
204///     // The other factory methods return typed builders
205///     // for common parameter types.
206///     ParamBuilder::query::<&A>(),
207/// )
208///     .build_state(&mut world)
209///     .build_system(my_system);
210/// ```
211#[derive(Default, Debug, Clone)]
212pub struct ParamBuilder;
213
214// SAFETY: Calls `SystemParam::init_state`
215unsafe impl<P: SystemParam> SystemParamBuilder<P> for ParamBuilder {
216    fn build(self, world: &mut World) -> P::State {
217        P::init_state(world)
218    }
219}
220
221impl ParamBuilder {
222    /// Creates a [`SystemParamBuilder`] for any [`SystemParam`] that uses its default initialization.
223    pub fn of<T: SystemParam>() -> impl SystemParamBuilder<T> {
224        Self
225    }
226
227    /// Helper method for reading a [`Resource`] as a param, equivalent to `of::<Res<T>>()`
228    pub fn resource<'w, T: Resource>() -> impl SystemParamBuilder<Res<'w, T>> {
229        Self
230    }
231
232    /// Helper method for mutably accessing a [`Resource`] as a param, equivalent to `of::<ResMut<T>>()`
233    pub fn resource_mut<'w, T: Resource<Mutability = Mutable>>(
234    ) -> impl SystemParamBuilder<ResMut<'w, T>> {
235        Self
236    }
237
238    /// Helper method for adding a [`Local`] as a param, equivalent to `of::<Local<T>>()`
239    pub fn local<'s, T: FromWorld + Send + 'static>() -> impl SystemParamBuilder<Local<'s, T>> {
240        Self
241    }
242
243    /// Helper method for adding a [`Query`] as a param, equivalent to `of::<Query<D>>()`
244    pub fn query<'w, 's, D: QueryData + 'static>() -> impl SystemParamBuilder<Query<'w, 's, D, ()>>
245    {
246        Self
247    }
248
249    /// Helper method for adding a filtered [`Query`] as a param, equivalent to `of::<Query<D, F>>()`
250    pub fn query_filtered<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>(
251    ) -> impl SystemParamBuilder<Query<'w, 's, D, F>> {
252        Self
253    }
254}
255
256/// A marker type used to distinguish builder systems from plain function systems.
257#[doc(hidden)]
258pub struct IsBuilderSystem;
259
260/// An [`IntoSystem`] creating an instance of [`BuilderSystem`]
261pub struct IntoBuilderSystem<Marker, In, Out, Func, Builder>
262where
263    Func: SystemParamFunction<Marker>,
264    Builder: SystemParamBuilder<Func::Param>,
265{
266    builder: Builder,
267    func: Func,
268    _marker: PhantomData<fn(In) -> (Marker, Out)>,
269}
270
271impl<Marker, In, Out, Func, Builder> IntoBuilderSystem<Marker, In, Out, Func, Builder>
272where
273    Func: SystemParamFunction<Marker>,
274    Builder: SystemParamBuilder<Func::Param>,
275{
276    /// Returns a new [`IntoBuilderSystem`] given a system param builder and system function
277    pub fn new(builder: Builder, func: Func) -> Self {
278        Self {
279            builder,
280            func,
281            _marker: PhantomData,
282        }
283    }
284}
285
286impl<Marker, In, Out, Func, Builder> IntoSystem<In, Out, (IsBuilderSystem, Marker)>
287    for IntoBuilderSystem<Marker, In, Out, Func, Builder>
288where
289    Marker: 'static,
290    In: SystemInput + 'static,
291    Out: 'static,
292    Func: SystemParamFunction<Marker, In: FromInput<In>, Out: IntoResult<Out>>,
293    Builder: SystemParamBuilder<Func::Param> + Send + Sync + 'static,
294{
295    type System = BuilderSystem<Marker, In, Out, Func, Builder>;
296
297    fn into_system(this: Self) -> Self::System {
298        BuilderSystem::new(this.builder, this.func)
299    }
300}
301
302/// A [`System`] created from a [`SystemParamBuilder`] whose state is not
303/// initialized until the first run.
304pub struct BuilderSystem<Marker, In, Out, Func, Builder>
305where
306    Func: SystemParamFunction<Marker>,
307    Builder: SystemParamBuilder<Func::Param>,
308{
309    inner: BuilderSystemInner<Marker, In, Out, Func, Builder>,
310}
311
312impl<Marker, In, Out, Func, Builder> BuilderSystem<Marker, In, Out, Func, Builder>
313where
314    Func: SystemParamFunction<Marker>,
315    Builder: SystemParamBuilder<Func::Param>,
316{
317    /// Returns a new `BuilderSystem` given a system param builder and a system function
318    pub fn new(builder: Builder, func: Func) -> Self {
319        Self {
320            inner: BuilderSystemInner::Uninitialized {
321                builder,
322                func,
323                meta: SystemMeta::new::<Func>(),
324            },
325        }
326    }
327}
328
329enum BuilderSystemInner<Marker, In, Out, Func, Builder>
330where
331    Func: SystemParamFunction<Marker>,
332    Builder: SystemParamBuilder<Func::Param>,
333{
334    /// A properly initialized system whose state has been constructed
335    Initialized {
336        system: FunctionSystem<Marker, In, Out, Func>,
337    },
338    /// An uninitialized system, whose state hasn't been constructed from
339    /// the param builder yet
340    Uninitialized {
341        builder: Builder,
342        func: Func,
343        meta: SystemMeta,
344    },
345    /// This only exists as a variant to use with `mem::replace` in `initialize`.
346    /// If this state is ever observed outside `initialize`, then a `panic!`
347    /// interrupted initialization, leaving this system in an invalid state.
348    Invalid,
349}
350
351impl<Marker, In, Out, Func, Builder> System for BuilderSystem<Marker, In, Out, Func, Builder>
352where
353    Marker: 'static,
354    In: SystemInput + 'static,
355    Out: 'static,
356    Func: SystemParamFunction<Marker, In: FromInput<In>, Out: IntoResult<Out>>,
357    Builder: SystemParamBuilder<Func::Param> + Send + Sync + 'static,
358{
359    type In = In;
360
361    type Out = Out;
362
363    #[inline]
364    fn name(&self) -> DebugName {
365        match &self.inner {
366            BuilderSystemInner::Initialized { system } => system.name(),
367            BuilderSystemInner::Uninitialized { meta, .. } => meta.name().clone(),
368            BuilderSystemInner::Invalid => unreachable!(),
369        }
370    }
371
372    #[inline]
373    fn flags(&self) -> SystemStateFlags {
374        match &self.inner {
375            BuilderSystemInner::Initialized { system, .. } => system.flags(),
376            BuilderSystemInner::Uninitialized { meta, .. } => meta.flags(),
377            BuilderSystemInner::Invalid => unreachable!(),
378        }
379    }
380
381    #[inline]
382    unsafe fn run_unsafe(
383        &mut self,
384        input: super::SystemIn<'_, Self>,
385        world: UnsafeWorldCell,
386    ) -> Result<Self::Out, RunSystemError> {
387        match &mut self.inner {
388            // SAFETY: requirements upheld by the caller.
389            BuilderSystemInner::Initialized { system, .. } => unsafe {
390                system.run_unsafe(input, world)
391            },
392            BuilderSystemInner::Uninitialized { .. } => panic!(
393                "BuilderSystem {} was not initialized before calling run_unsafe.",
394                self.name()
395            ),
396            BuilderSystemInner::Invalid => unreachable!(),
397        }
398    }
399
400    #[cfg(feature = "hotpatching")]
401    #[inline]
402    fn refresh_hotpatch(&mut self) {
403        match &mut self.inner {
404            BuilderSystemInner::Initialized { system, .. } => system.refresh_hotpatch(),
405            BuilderSystemInner::Uninitialized { .. } => {}
406            BuilderSystemInner::Invalid => unreachable!(),
407        }
408    }
409
410    #[inline]
411    fn apply_deferred(&mut self, world: &mut World) {
412        match &mut self.inner {
413            BuilderSystemInner::Initialized { system, .. } => system.apply_deferred(world),
414            BuilderSystemInner::Uninitialized { .. } => {}
415            BuilderSystemInner::Invalid => unreachable!(),
416        }
417    }
418
419    #[inline]
420    fn queue_deferred(&mut self, world: DeferredWorld) {
421        match &mut self.inner {
422            BuilderSystemInner::Initialized { system, .. } => system.queue_deferred(world),
423            BuilderSystemInner::Uninitialized { .. } => {}
424            BuilderSystemInner::Invalid => unreachable!(),
425        }
426    }
427
428    #[inline]
429    fn initialize(&mut self, world: &mut World) -> SystemAccess {
430        let inner = mem::replace(&mut self.inner, BuilderSystemInner::Invalid);
431        match inner {
432            BuilderSystemInner::Initialized { mut system } => {
433                let access = system.initialize(world);
434                self.inner = BuilderSystemInner::Initialized { system };
435                access
436            }
437            BuilderSystemInner::Uninitialized { builder, func, .. } => {
438                let mut system = builder.build_state(world).build_any_system(func);
439                let access = system.initialize(world);
440                self.inner = BuilderSystemInner::Initialized { system };
441                access
442            }
443            BuilderSystemInner::Invalid => unreachable!(),
444        }
445    }
446
447    #[inline]
448    fn check_change_tick(&mut self, check: CheckChangeTicks) {
449        match &mut self.inner {
450            BuilderSystemInner::Initialized { system, .. } => system.check_change_tick(check),
451            BuilderSystemInner::Uninitialized { .. } => {}
452            BuilderSystemInner::Invalid => unreachable!(),
453        }
454    }
455
456    #[inline]
457    fn get_last_run(&self) -> Tick {
458        match &self.inner {
459            BuilderSystemInner::Initialized { system, .. } => system.get_last_run(),
460            BuilderSystemInner::Uninitialized { meta, .. } => meta.get_last_run(),
461            BuilderSystemInner::Invalid => unreachable!(),
462        }
463    }
464
465    #[inline]
466    fn set_last_run(&mut self, last_run: Tick) {
467        match &mut self.inner {
468            BuilderSystemInner::Initialized { system, .. } => system.set_last_run(last_run),
469            BuilderSystemInner::Uninitialized { meta, .. } => meta.set_last_run(last_run),
470            BuilderSystemInner::Invalid => unreachable!(),
471        }
472    }
473}
474
475// SAFETY: if the wrapped system is read-only, so is this one
476unsafe impl<Marker, In, Out, Func, Builder> ReadOnlySystem
477    for BuilderSystem<Marker, In, Out, Func, Builder>
478where
479    Marker: 'static,
480    In: SystemInput + 'static,
481    Out: 'static,
482    Func: SystemParamFunction<Marker, In: FromInput<In>, Out: IntoResult<Out>>,
483    Builder: SystemParamBuilder<Func::Param> + Send + Sync + 'static,
484    // the important bound
485    FunctionSystem<Marker, In, Out, Func>: ReadOnlySystem,
486{
487}
488
489// SAFETY: Any `QueryState<D, F>` for the correct world is valid for `Query::State`,
490// and we check the world during `build`.
491unsafe impl<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>
492    SystemParamBuilder<Query<'w, 's, D, F>> for QueryState<D, F>
493{
494    fn build(self, world: &mut World) -> QueryState<D, F> {
495        self.validate_world(world.id());
496        self
497    }
498}
499
500/// A [`SystemParamBuilder`] for a [`Query`].
501/// This takes a closure accepting an `&mut` [`QueryBuilder`] and uses the builder to construct the query's state.
502/// This can be used to add additional filters,
503/// or to configure the components available to [`FilteredEntityRef`](crate::world::FilteredEntityRef) or [`FilteredEntityMut`](crate::world::FilteredEntityMut).
504///
505/// ## Example
506///
507/// ```
508/// # use bevy_ecs::{
509/// #     prelude::*,
510/// #     system::{SystemParam, QueryParamBuilder},
511/// # };
512/// #
513/// # #[derive(Component)]
514/// # struct Player;
515/// #
516/// # let mut world = World::new();
517/// let system = (QueryParamBuilder::new(|builder| {
518///     builder.with::<Player>();
519/// }),)
520///     .build_state(&mut world)
521///     .build_system(|query: Query<()>| {
522///         for _ in &query {
523///             // This only includes entities with a `Player` component.
524///         }
525///     });
526///
527/// // When collecting multiple builders into a `Vec`,
528/// // use `new_box()` to erase the closure type.
529/// let system = (vec![
530///     QueryParamBuilder::new_box(|builder| {
531///         builder.with::<Player>();
532///     }),
533///     QueryParamBuilder::new_box(|builder| {
534///         builder.without::<Player>();
535///     }),
536/// ],)
537///     .build_state(&mut world)
538///     .build_system(|query: Vec<Query<()>>| {});
539/// ```
540#[derive(Clone)]
541pub struct QueryParamBuilder<T>(T);
542
543impl<T> QueryParamBuilder<T> {
544    /// Creates a [`SystemParamBuilder`] for a [`Query`] that accepts a callback to configure the [`QueryBuilder`].
545    pub fn new<D: QueryData, F: QueryFilter>(f: T) -> Self
546    where
547        T: FnOnce(&mut QueryBuilder<D, F>),
548    {
549        Self(f)
550    }
551}
552
553impl<'a, D: QueryData, F: QueryFilter>
554    QueryParamBuilder<Box<dyn FnOnce(&mut QueryBuilder<D, F>) + 'a>>
555{
556    /// Creates a [`SystemParamBuilder`] for a [`Query`] that accepts a callback to configure the [`QueryBuilder`].
557    /// This boxes the callback so that it has a common type and can be put in a `Vec`.
558    pub fn new_box(f: impl FnOnce(&mut QueryBuilder<D, F>) + 'a) -> Self {
559        Self(Box::new(f))
560    }
561}
562
563// SAFETY: Any `QueryState<D, F>` for the correct world is valid for `Query::State`,
564// and `QueryBuilder` produces one with the given `world`.
565unsafe impl<
566        'w,
567        's,
568        D: QueryData + 'static,
569        F: QueryFilter + 'static,
570        T: FnOnce(&mut QueryBuilder<D, F>),
571    > SystemParamBuilder<Query<'w, 's, D, F>> for QueryParamBuilder<T>
572{
573    fn build(self, world: &mut World) -> QueryState<D, F> {
574        let mut builder = QueryBuilder::new(world);
575        (self.0)(&mut builder);
576        builder.build()
577    }
578}
579
580macro_rules! impl_system_param_builder_tuple {
581    ($(#[$meta:meta])* $(($param: ident, $builder: ident)),*) => {
582        #[expect(
583            clippy::allow_attributes,
584            reason = "This is in a macro; as such, the below lints may not always apply."
585        )]
586        #[allow(
587            unused_variables,
588            reason = "Zero-length tuples won't use any of the parameters."
589        )]
590        #[allow(
591            non_snake_case,
592            reason = "The variable names are provided by the macro caller, not by us."
593        )]
594        $(#[$meta])*
595        // SAFETY: implementors of each `SystemParamBuilder` in the tuple have validated their impls
596        unsafe impl<$($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<($($param,)*)> for ($($builder,)*) {
597            fn build(self, world: &mut World) -> <($($param,)*) as SystemParam>::State {
598                let ($($builder,)*) = self;
599                #[allow(
600                    clippy::unused_unit,
601                    reason = "Zero-length tuples won't generate any calls to the system parameter builders."
602                )]
603                ($($builder.build(world),)*)
604            }
605        }
606    };
607}
608
609all_tuples!(
610    #[doc(fake_variadic)]
611    impl_system_param_builder_tuple,
612    0,
613    16,
614    P,
615    B
616);
617
618// SAFETY: implementors of each `SystemParamBuilder` in the vec have validated their impls
619unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<Vec<P>> for Vec<B> {
620    fn build(self, world: &mut World) -> <Vec<P> as SystemParam>::State {
621        self.into_iter()
622            .map(|builder| builder.build(world))
623            .collect()
624    }
625}
626
627// SAFETY: implementors of each `SystemParamBuilder` in the vec have validated their impls
628unsafe impl<P: SystemParam, B: SystemParamBuilder<P>, const N: usize>
629    SystemParamBuilder<SmallVec<[P; N]>> for SmallVec<[B; N]>
630{
631    fn build(self, world: &mut World) -> <SmallVec<[P; N]> as SystemParam>::State {
632        self.into_iter()
633            .map(|builder| builder.build(world))
634            .collect()
635    }
636}
637
638/// A [`SystemParamBuilder`] for a [`ParamSet`].
639///
640/// To build a [`ParamSet`] with a tuple of system parameters, pass a tuple of matching [`SystemParamBuilder`]s.
641/// To build a [`ParamSet`] with a [`Vec`] of system parameters, pass a `Vec` of matching [`SystemParamBuilder`]s.
642///
643/// # Examples
644///
645/// ```
646/// # use bevy_ecs::{prelude::*, system::*};
647/// #
648/// # #[derive(Component)]
649/// # struct Health;
650/// #
651/// # #[derive(Component)]
652/// # struct Enemy;
653/// #
654/// # #[derive(Component)]
655/// # struct Ally;
656/// #
657/// # let mut world = World::new();
658/// #
659/// let system = (ParamSetBuilder((
660///     QueryParamBuilder::new(|builder| {
661///         builder.with::<Enemy>();
662///     }),
663///     QueryParamBuilder::new(|builder| {
664///         builder.with::<Ally>();
665///     }),
666///     ParamBuilder,
667/// )),)
668///     .build_state(&mut world)
669///     .build_system(buildable_system_with_tuple);
670/// # world.run_system_once(system);
671///
672/// fn buildable_system_with_tuple(
673///     mut set: ParamSet<(Query<&mut Health>, Query<&mut Health>, &World)>,
674/// ) {
675///     // The first parameter is built from the first builder,
676///     // so this will iterate over enemies.
677///     for mut health in set.p0().iter_mut() {}
678///     // And the second parameter is built from the second builder,
679///     // so this will iterate over allies.
680///     for mut health in set.p1().iter_mut() {}
681///     // Parameters that don't need special building can use `ParamBuilder`.
682///     let entities = set.p2().entities();
683/// }
684///
685/// let system = (ParamSetBuilder(vec![
686///     QueryParamBuilder::new_box(|builder| {
687///         builder.with::<Enemy>();
688///     }),
689///     QueryParamBuilder::new_box(|builder| {
690///         builder.with::<Ally>();
691///     }),
692/// ]),)
693///     .build_state(&mut world)
694///     .build_system(buildable_system_with_vec);
695/// # world.run_system_once(system);
696///
697/// fn buildable_system_with_vec(mut set: ParamSet<Vec<Query<&mut Health>>>) {
698///     // As with tuples, the first parameter is built from the first builder,
699///     // so this will iterate over enemies.
700///     for mut health in set.get_mut(0).iter_mut() {}
701///     // And the second parameter is built from the second builder,
702///     // so this will iterate over allies.
703///     for mut health in set.get_mut(1).iter_mut() {}
704///     // You can iterate over the parameters either by index,
705///     // or using the `for_each` method.
706///     set.for_each(|mut query| for mut health in query.iter_mut() {});
707/// }
708/// ```
709#[derive(Debug, Default, Clone)]
710pub struct ParamSetBuilder<T>(pub T);
711
712macro_rules! impl_param_set_builder_tuple {
713    ($(($param: ident, $builder: ident)),*) => {
714        #[expect(
715            clippy::allow_attributes,
716            reason = "This is in a macro; as such, the below lints may not always apply."
717        )]
718        #[allow(
719            unused_variables,
720            reason = "Zero-length tuples won't use any of the parameters."
721        )]
722        #[allow(
723            non_snake_case,
724            reason = "The variable names are provided by the macro caller, not by us."
725        )]
726        // SAFETY: implementors of each `SystemParamBuilder` in the tuple have validated their impls
727        unsafe impl<'w, 's, $($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<ParamSet<'w, 's, ($($param,)*)>> for ParamSetBuilder<($($builder,)*)> {
728            fn build(self, world: &mut World) -> <($($param,)*) as SystemParam>::State {
729                let ParamSetBuilder(($($builder,)*)) = self;
730                ($($builder.build(world),)*)
731            }
732        }
733    };
734}
735
736all_tuples!(impl_param_set_builder_tuple, 1, 8, P, B);
737
738// SAFETY: implementors of each `SystemParamBuilder` in the vec have validated their impls
739unsafe impl<'w, 's, P: SystemParam, B: SystemParamBuilder<P>>
740    SystemParamBuilder<ParamSet<'w, 's, Vec<P>>> for ParamSetBuilder<Vec<B>>
741{
742    fn build(self, world: &mut World) -> <Vec<P> as SystemParam>::State {
743        self.0
744            .into_iter()
745            .map(|builder| builder.build(world))
746            .collect()
747    }
748}
749
750/// A [`SystemParamBuilder`] for a [`DynSystemParam`].
751/// See the [`DynSystemParam`] docs for examples.
752pub struct DynParamBuilder<'a>(Box<dyn FnOnce(&mut World) -> DynSystemParamState + 'a>);
753
754impl<'a> DynParamBuilder<'a> {
755    /// Creates a new [`DynParamBuilder`] by wrapping a [`SystemParamBuilder`] of any type.
756    /// The built [`DynSystemParam`] can be downcast to `T`.
757    pub fn new<T: SystemParam + 'static>(builder: impl SystemParamBuilder<T> + 'a) -> Self {
758        Self(Box::new(|world| {
759            DynSystemParamState::new::<T>(builder.build(world))
760        }))
761    }
762}
763
764// SAFETY: `DynSystemParam::get_param` will call `get_param` on the boxed `DynSystemParamState`,
765// and the boxed builder was a valid implementation of `SystemParamBuilder` for that type.
766// The resulting `DynSystemParam` can only perform access by downcasting to that param type.
767unsafe impl<'a, 'w, 's> SystemParamBuilder<DynSystemParam<'w, 's>> for DynParamBuilder<'a> {
768    fn build(self, world: &mut World) -> <DynSystemParam<'w, 's> as SystemParam>::State {
769        (self.0)(world)
770    }
771}
772
773/// A [`SystemParamBuilder`] for a [`Local`].
774/// The provided value will be used as the initial value of the `Local`.
775///
776/// ## Example
777///
778/// ```
779/// # use bevy_ecs::{
780/// #     prelude::*,
781/// #     system::{SystemParam, LocalBuilder, RunSystemOnce},
782/// # };
783/// #
784/// # let mut world = World::new();
785/// let system = (LocalBuilder(100),)
786///     .build_state(&mut world)
787///     .build_system(|local: Local<usize>| {
788///         assert_eq!(*local, 100);
789///     });
790/// # world.run_system_once(system);
791/// ```
792#[derive(Default, Debug, Clone)]
793pub struct LocalBuilder<T>(pub T);
794
795// SAFETY: Any value of `T` is a valid state for `Local`.
796unsafe impl<'s, T: FromWorld + Send + 'static> SystemParamBuilder<Local<'s, T>>
797    for LocalBuilder<T>
798{
799    fn build(self, _world: &mut World) -> <Local<'s, T> as SystemParam>::State {
800        SyncCell::new(self.0)
801    }
802}
803
804/// A [`SystemParamBuilder`] for a [`FilteredResources`].
805/// See the [`FilteredResources`] docs for examples.
806#[derive(Clone)]
807#[deprecated(since = "0.20.0", note = "Use `QueryParamBuilder` instead.")]
808pub struct FilteredResourcesParamBuilder<T>(T);
809
810#[expect(
811    deprecated,
812    reason = "`FilteredResourcesParamBuilder` will be removed."
813)]
814impl<T> FilteredResourcesParamBuilder<T> {
815    /// Creates a [`SystemParamBuilder`] for a [`FilteredResources`] that accepts a callback to configure the [`FilteredResourcesBuilder`].
816    pub fn new(f: T) -> Self
817    where
818        T: FnOnce(&mut FilteredResourcesBuilder),
819    {
820        Self(f)
821    }
822}
823
824#[expect(
825    deprecated,
826    reason = "`FilteredResourcesParamBuilder` will be removed."
827)]
828impl<'a> FilteredResourcesParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesBuilder) + 'a>> {
829    /// Creates a [`SystemParamBuilder`] for a [`FilteredResources`] that accepts a callback to configure the [`FilteredResourcesBuilder`].
830    /// This boxes the callback so that it has a common type.
831    pub fn new_box(f: impl FnOnce(&mut FilteredResourcesBuilder) + 'a) -> Self {
832        Self(Box::new(f))
833    }
834}
835
836#[expect(
837    deprecated,
838    reason = "`FilteredResourcesParamBuilder` will be removed."
839)]
840// SAFETY: Any `Access` is a valid state for `FilteredResources`.
841unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesBuilder)>
842    SystemParamBuilder<FilteredResources<'w, 's>> for FilteredResourcesParamBuilder<T>
843{
844    fn build(self, world: &mut World) -> <FilteredResources<'w, 's> as SystemParam>::State {
845        let mut builder = FilteredResourcesBuilder::new(world);
846        (self.0)(&mut builder);
847        builder.build()
848    }
849}
850
851/// A [`SystemParamBuilder`] for a [`FilteredResourcesMut`].
852/// See the [`FilteredResourcesMut`] docs for examples.
853#[derive(Clone)]
854#[deprecated(since = "0.20.0", note = "Use `QueryParamBuilder` instead.")]
855pub struct FilteredResourcesMutParamBuilder<T>(T);
856
857#[expect(
858    deprecated,
859    reason = "`FilteredResourcesMutParamBuilder` will be removed."
860)]
861impl<T> FilteredResourcesMutParamBuilder<T> {
862    /// Creates a [`SystemParamBuilder`] for a [`FilteredResourcesMut`] that accepts a callback to configure the [`FilteredResourcesMutBuilder`].
863    pub fn new(f: T) -> Self
864    where
865        T: FnOnce(&mut FilteredResourcesMutBuilder),
866    {
867        Self(f)
868    }
869}
870
871#[expect(
872    deprecated,
873    reason = "`FilteredResourcesMutParamBuilder` will be removed."
874)]
875impl<'a> FilteredResourcesMutParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesMutBuilder) + 'a>> {
876    /// Creates a [`SystemParamBuilder`] for a [`FilteredResourcesMut`] that accepts a callback to configure the [`FilteredResourcesMutBuilder`].
877    /// This boxes the callback so that it has a common type.
878    pub fn new_box(f: impl FnOnce(&mut FilteredResourcesMutBuilder) + 'a) -> Self {
879        Self(Box::new(f))
880    }
881}
882
883#[expect(
884    deprecated,
885    reason = "`FilteredResourcesMutParamBuilder` will be removed."
886)]
887// SAFETY: Any `Access` is a valid state for `FilteredResourcesMut`.
888unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesMutBuilder)>
889    SystemParamBuilder<FilteredResourcesMut<'w, 's>> for FilteredResourcesMutParamBuilder<T>
890{
891    fn build(self, world: &mut World) -> <FilteredResourcesMut<'w, 's> as SystemParam>::State {
892        let mut builder = FilteredResourcesMutBuilder::new(world);
893        (self.0)(&mut builder);
894        builder.build()
895    }
896}
897
898/// A [`SystemParamBuilder`] for an [`Option`].
899#[derive(Clone)]
900pub struct OptionBuilder<T>(T);
901
902// SAFETY: `OptionBuilder<B>` builds a state that is valid for `P`, and any state valid for `P` is valid for `Option<P>`
903unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<Option<P>>
904    for OptionBuilder<B>
905{
906    fn build(self, world: &mut World) -> <Option<P> as SystemParam>::State {
907        self.0.build(world)
908    }
909}
910
911/// A [`SystemParamBuilder`] for a [`Result`] of [`SystemParamValidationError`].
912#[derive(Clone)]
913pub struct ResultBuilder<T>(T);
914
915// SAFETY: `ResultBuilder<B>` builds a state that is valid for `P`, and any state valid for `P` is valid for `Result<P, SystemParamValidationError>`
916unsafe impl<P: SystemParam, B: SystemParamBuilder<P>>
917    SystemParamBuilder<Result<P, SystemParamValidationError>> for ResultBuilder<B>
918{
919    fn build(
920        self,
921        world: &mut World,
922    ) -> <Result<P, SystemParamValidationError> as SystemParam>::State {
923        self.0.build(world)
924    }
925}
926
927/// A [`SystemParamBuilder`] for a [`If`].
928#[derive(Clone)]
929pub struct IfBuilder<T>(T);
930
931// SAFETY: `IfBuilder<B>` builds a state that is valid for `P`, and any state valid for `P` is valid for `If<P>`
932unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<If<P>> for IfBuilder<B> {
933    fn build(self, world: &mut World) -> <If<P> as SystemParam>::State {
934        self.0.build(world)
935    }
936}
937
938#[cfg(test)]
939mod tests {
940    use crate::{
941        entity::Entities,
942        error::Result,
943        prelude::{Component, Query},
944        reflect::ReflectResource,
945        system::{Local, RunSystemOnce},
946    };
947    use alloc::vec;
948    use bevy_reflect::Reflect;
949
950    use super::*;
951
952    #[derive(Component)]
953    struct A;
954
955    #[derive(Component)]
956    struct B;
957
958    #[derive(Component)]
959    struct C;
960
961    #[derive(Resource, Default, Reflect)]
962    #[reflect(Resource)]
963    struct R {
964        foo: usize,
965    }
966
967    fn local_system(local: Local<u64>) -> u64 {
968        *local
969    }
970
971    fn query_system(query: Query<()>) -> usize {
972        query.iter().count()
973    }
974
975    fn query_system_result(query: Query<()>) -> Result<usize> {
976        Ok(query.iter().count())
977    }
978
979    fn multi_param_system(a: Local<u64>, b: Local<u64>) -> u64 {
980        *a + *b + 1
981    }
982
983    #[test]
984    fn local_builder() {
985        let mut world = World::new();
986
987        let system = (LocalBuilder(10),)
988            .build_state(&mut world)
989            .build_system(local_system);
990
991        let output = world.run_system_once(system).unwrap();
992        assert_eq!(output, 10);
993
994        let builder_system = (LocalBuilder(10),).build_system(local_system);
995
996        let output = world.run_system_once(builder_system).unwrap();
997        assert_eq!(output, 10);
998    }
999
1000    #[test]
1001    fn query_builder() {
1002        let mut world = World::new();
1003
1004        world.spawn(A);
1005        world.spawn_empty();
1006
1007        let system = (QueryParamBuilder::new(|query| {
1008            query.with::<A>();
1009        }),)
1010            .build_state(&mut world)
1011            .build_system(query_system);
1012
1013        let output = world.run_system_once(system).unwrap();
1014        assert_eq!(output, 1);
1015
1016        let builder_system = (QueryParamBuilder::new(|query| {
1017            query.with::<A>();
1018        }),)
1019            .build_system(query_system);
1020
1021        let output = world.run_system_once(builder_system).unwrap();
1022        assert_eq!(output, 1);
1023    }
1024
1025    #[test]
1026    fn query_builder_system_result_fallible() {
1027        let mut world = World::new();
1028
1029        world.spawn(A);
1030        world.spawn_empty();
1031
1032        let system = (QueryParamBuilder::new(|query| {
1033            query.with::<A>();
1034        }),)
1035            .build_state(&mut world)
1036            .build_system(query_system_result);
1037
1038        // The type annotation here is necessary since the system
1039        // could also return `Result<usize>`
1040        let output: usize = world.run_system_once(system).unwrap();
1041        assert_eq!(output, 1);
1042
1043        let builder_system = (QueryParamBuilder::new(|query| {
1044            query.with::<A>();
1045        }),)
1046            .build_system(query_system_result);
1047
1048        // The type annotation here is necessary since the system
1049        // could also return `Result<usize>`
1050        let output: usize = world.run_system_once(builder_system).unwrap();
1051        assert_eq!(output, 1);
1052    }
1053
1054    #[test]
1055    fn query_builder_result_infallible() {
1056        let mut world = World::new();
1057
1058        world.spawn(A);
1059        world.spawn_empty();
1060
1061        let system = (QueryParamBuilder::new(|query| {
1062            query.with::<A>();
1063        }),)
1064            .build_state(&mut world)
1065            .build_system(query_system_result);
1066
1067        // The type annotation here is necessary since the system
1068        // could also return `usize`
1069        let output: Result<usize> = world.run_system_once(system).unwrap();
1070        assert_eq!(output.unwrap(), 1);
1071
1072        let builder_system = (QueryParamBuilder::new(|query| {
1073            query.with::<A>();
1074        }),)
1075            .build_system(query_system_result);
1076
1077        // The type annotation here is necessary since the system
1078        // could also return `usize`
1079        let output: Result<usize> = world.run_system_once(builder_system).unwrap();
1080        assert_eq!(output.unwrap(), 1);
1081    }
1082
1083    #[test]
1084    fn query_builder_state() {
1085        let mut world = World::new();
1086
1087        world.spawn(A);
1088        world.spawn_empty();
1089
1090        let state = QueryBuilder::new(&mut world).with::<A>().build();
1091
1092        let system = (state,).build_state(&mut world).build_system(query_system);
1093
1094        let output = world.run_system_once(system).unwrap();
1095        assert_eq!(output, 1);
1096
1097        let state = QueryBuilder::new(&mut world).with::<A>().build();
1098
1099        let builder_system = (state,).build_system(query_system);
1100
1101        let output = world.run_system_once(builder_system).unwrap();
1102        assert_eq!(output, 1);
1103    }
1104
1105    #[test]
1106    fn multi_param_builder() {
1107        let mut world = World::new();
1108
1109        world.spawn(A);
1110        world.spawn_empty();
1111
1112        let system = (LocalBuilder(0), ParamBuilder)
1113            .build_state(&mut world)
1114            .build_system(multi_param_system);
1115
1116        let output = world.run_system_once(system).unwrap();
1117        assert_eq!(output, 1);
1118
1119        let builder_system = (LocalBuilder(0), ParamBuilder).build_system(multi_param_system);
1120
1121        let output = world.run_system_once(builder_system).unwrap();
1122        assert_eq!(output, 1);
1123    }
1124
1125    #[test]
1126    fn vec_builder() {
1127        let mut world = World::new();
1128
1129        world.spawn((A, B, C));
1130        world.spawn((A, B));
1131        world.spawn((A, C));
1132        world.spawn((A, C));
1133        world.spawn_empty();
1134
1135        let system = (vec![
1136            QueryParamBuilder::new_box(|builder| {
1137                builder.with::<B>().without::<C>();
1138            }),
1139            QueryParamBuilder::new_box(|builder| {
1140                builder.with::<C>().without::<B>();
1141            }),
1142        ],)
1143            .build_state(&mut world)
1144            .build_system(|params: Vec<Query<&mut A>>| {
1145                let mut count: usize = 0;
1146                params
1147                    .into_iter()
1148                    .for_each(|mut query| count += query.iter_mut().count());
1149                count
1150            });
1151
1152        // NOTE: this isn't compatible with `BuilderSystem`, because the system param builder isn't 'static
1153
1154        let output = world.run_system_once(system).unwrap();
1155        assert_eq!(output, 3);
1156    }
1157
1158    #[test]
1159    fn multi_param_builder_inference() {
1160        let mut world = World::new();
1161
1162        world.spawn(A);
1163        world.spawn_empty();
1164
1165        let system = (LocalBuilder(0u64), ParamBuilder::local::<u64>())
1166            .build_state(&mut world)
1167            .build_system(|a, b| *a + *b + 1);
1168
1169        // NOTE: this isn't compatible with `BuilderSystem`, because it uses parameter type inference
1170
1171        let output = world.run_system_once(system).unwrap();
1172        assert_eq!(output, 1);
1173    }
1174
1175    #[test]
1176    fn param_set_builder() {
1177        let mut world = World::new();
1178
1179        world.spawn((A, B, C));
1180        world.spawn((A, B));
1181        world.spawn((A, C));
1182        world.spawn((A, C));
1183        world.spawn_empty();
1184
1185        let system = (ParamSetBuilder((
1186            QueryParamBuilder::new(|builder| {
1187                builder.with::<B>();
1188            }),
1189            QueryParamBuilder::new(|builder| {
1190                builder.with::<C>();
1191            }),
1192        )),)
1193            .build_state(&mut world)
1194            .build_system(|mut params: ParamSet<(Query<&mut A>, Query<&mut A>)>| {
1195                params.p0().iter().count() + params.p1().iter().count()
1196            });
1197
1198        let output = world.run_system_once(system).unwrap();
1199        assert_eq!(output, 5);
1200
1201        let builder_system = (ParamSetBuilder((
1202            QueryParamBuilder::new(|builder| {
1203                builder.with::<B>();
1204            }),
1205            QueryParamBuilder::new(|builder| {
1206                builder.with::<C>();
1207            }),
1208        )),)
1209            .build_system(|mut params: ParamSet<(Query<&mut A>, Query<&mut A>)>| {
1210                params.p0().iter().count() + params.p1().iter().count()
1211            });
1212
1213        let output = world.run_system_once(builder_system).unwrap();
1214        assert_eq!(output, 5);
1215    }
1216
1217    #[test]
1218    fn param_set_vec_builder() {
1219        let mut world = World::new();
1220
1221        world.spawn((A, B, C));
1222        world.spawn((A, B));
1223        world.spawn((A, C));
1224        world.spawn((A, C));
1225        world.spawn_empty();
1226
1227        let system = (ParamSetBuilder(vec![
1228            QueryParamBuilder::new_box(|builder| {
1229                builder.with::<B>();
1230            }),
1231            QueryParamBuilder::new_box(|builder| {
1232                builder.with::<C>();
1233            }),
1234        ]),)
1235            .build_state(&mut world)
1236            .build_system(|mut params: ParamSet<Vec<Query<&mut A>>>| {
1237                let mut count = 0;
1238                params.for_each(|mut query| count += query.iter_mut().count());
1239                count
1240            });
1241
1242        // NOTE: this isn't compatible with `BuilderSystem`, because the system param builder isn't 'static
1243
1244        let output = world.run_system_once(system).unwrap();
1245        assert_eq!(output, 5);
1246    }
1247
1248    #[test]
1249    fn dyn_builder() {
1250        let mut world = World::new();
1251
1252        world.spawn(A);
1253        world.spawn_empty();
1254
1255        let system = (
1256            DynParamBuilder::new(LocalBuilder(3_usize)),
1257            DynParamBuilder::new::<Query<()>>(QueryParamBuilder::new(|builder| {
1258                builder.with::<A>();
1259            })),
1260            DynParamBuilder::new::<&Entities>(ParamBuilder),
1261        )
1262            .build_state(&mut world)
1263            .build_system(
1264                |mut p0: DynSystemParam, mut p1: DynSystemParam, mut p2: DynSystemParam| {
1265                    let local = *p0.downcast_mut::<Local<usize>>().unwrap();
1266                    let query_count = p1.downcast_mut::<Query<()>>().unwrap().iter().count();
1267                    let _entities = p2.downcast_mut::<&Entities>().unwrap();
1268                    assert!(p0.downcast_mut::<Query<()>>().is_none());
1269                    local + query_count
1270                },
1271            );
1272
1273        // NOTE: this isn't compatible with `BuilderSystem`, because the system param builder isn't 'static
1274
1275        let output = world.run_system_once(system).unwrap();
1276        assert_eq!(output, 4);
1277    }
1278
1279    #[derive(SystemParam)]
1280    #[system_param(builder)]
1281    struct CustomParam<'w, 's> {
1282        query: Query<'w, 's, ()>,
1283        local: Local<'s, usize>,
1284    }
1285
1286    #[test]
1287    fn custom_param_builder() {
1288        let mut world = World::new();
1289
1290        world.spawn(A);
1291        world.spawn_empty();
1292
1293        let system = (CustomParamBuilder {
1294            local: LocalBuilder(100),
1295            query: QueryParamBuilder::new(|builder| {
1296                builder.with::<A>();
1297            }),
1298        },)
1299            .build_state(&mut world)
1300            .build_system(|param: CustomParam| *param.local + param.query.iter().count());
1301
1302        let output = world.run_system_once(system).unwrap();
1303        assert_eq!(output, 101);
1304
1305        let builder_system = (CustomParamBuilder {
1306            local: LocalBuilder(100),
1307            query: QueryParamBuilder::new(|builder| {
1308                builder.with::<A>();
1309            }),
1310        },)
1311            .build_system(|param: CustomParam| *param.local + param.query.iter().count());
1312
1313        let output = world.run_system_once(builder_system).unwrap();
1314        assert_eq!(output, 101);
1315    }
1316
1317    #[test]
1318    #[expect(deprecated, reason = "`FilteredResources` will be removed.")]
1319    fn filtered_resource_conflicts_read_with_res() {
1320        let mut world = World::new();
1321        (
1322            ParamBuilder::resource(),
1323            FilteredResourcesParamBuilder::new(|builder| {
1324                builder.add_read::<R>();
1325            }),
1326        )
1327            .build_state(&mut world)
1328            .build_system(|_r: Res<R>, _fr: FilteredResources| {});
1329    }
1330
1331    #[test]
1332    #[should_panic]
1333    #[expect(deprecated, reason = "`FilteredResources` will be removed.")]
1334    fn filtered_resource_conflicts_read_with_resmut() {
1335        let mut world = World::new();
1336        (
1337            ParamBuilder::resource_mut(),
1338            FilteredResourcesParamBuilder::new(|builder| {
1339                builder.add_read::<R>();
1340            }),
1341        )
1342            .build_state(&mut world)
1343            .build_system(|_r: ResMut<R>, _fr: FilteredResources| {});
1344    }
1345
1346    #[test]
1347    #[should_panic]
1348    #[expect(deprecated, reason = "`FilteredResources` will be removed.")]
1349    fn filtered_resource_conflicts_read_all_with_resmut() {
1350        let mut world = World::new();
1351        (
1352            ParamBuilder::resource_mut(),
1353            FilteredResourcesParamBuilder::new(|builder| {
1354                builder.add_read_all();
1355            }),
1356        )
1357            .build_state(&mut world)
1358            .build_system(|_r: ResMut<R>, _fr: FilteredResources| {});
1359    }
1360
1361    #[test]
1362    #[expect(deprecated, reason = "`FilteredResourcesMut` will be removed.")]
1363    fn filtered_resource_mut_conflicts_read_with_res() {
1364        let mut world = World::new();
1365        (
1366            ParamBuilder::resource(),
1367            FilteredResourcesMutParamBuilder::new(|builder| {
1368                builder.add_read::<R>();
1369            }),
1370        )
1371            .build_state(&mut world)
1372            .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1373    }
1374
1375    #[test]
1376    #[should_panic]
1377    #[expect(deprecated, reason = "`FilteredResourcesMut` will be removed.")]
1378    fn filtered_resource_mut_conflicts_read_with_resmut() {
1379        let mut world = World::new();
1380        (
1381            ParamBuilder::resource_mut(),
1382            FilteredResourcesMutParamBuilder::new(|builder| {
1383                builder.add_read::<R>();
1384            }),
1385        )
1386            .build_state(&mut world)
1387            .build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
1388    }
1389
1390    #[test]
1391    #[should_panic]
1392    #[expect(deprecated, reason = "`FilteredResourcesMut` will be removed.")]
1393    fn filtered_resource_mut_conflicts_write_with_res() {
1394        let mut world = World::new();
1395        (
1396            ParamBuilder::resource(),
1397            FilteredResourcesMutParamBuilder::new(|builder| {
1398                builder.add_write::<R>();
1399            }),
1400        )
1401            .build_state(&mut world)
1402            .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1403    }
1404
1405    #[test]
1406    #[should_panic]
1407    #[expect(deprecated, reason = "`FilteredResourcesMut` will be removed.")]
1408    fn filtered_resource_mut_conflicts_write_all_with_res() {
1409        let mut world = World::new();
1410        (
1411            ParamBuilder::resource(),
1412            FilteredResourcesMutParamBuilder::new(|builder| {
1413                builder.add_write_all();
1414            }),
1415        )
1416            .build_state(&mut world)
1417            .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1418    }
1419
1420    #[test]
1421    #[should_panic]
1422    #[expect(deprecated, reason = "`FilteredResourcesMut` will be removed.")]
1423    fn filtered_resource_mut_conflicts_write_with_resmut() {
1424        let mut world = World::new();
1425        (
1426            ParamBuilder::resource_mut(),
1427            FilteredResourcesMutParamBuilder::new(|builder| {
1428                builder.add_write::<R>();
1429            }),
1430        )
1431            .build_state(&mut world)
1432            .build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
1433    }
1434}