use alloc::{boxed::Box, vec::Vec};1use bevy_platform::cell::SyncCell;2use variadics_please::all_tuples;34use crate::{5prelude::QueryBuilder,6query::{QueryData, QueryFilter, QueryState},7resource::Resource,8system::{9DynSystemParam, DynSystemParamState, If, Local, ParamSet, Query, SystemParam,10SystemParamValidationError,11},12world::{13FilteredResources, FilteredResourcesBuilder, FilteredResourcesMut,14FilteredResourcesMutBuilder, FromWorld, World,15},16};17use core::fmt::Debug;1819use super::{Res, ResMut, SystemState};2021/// A builder that can create a [`SystemParam`].22///23/// ```24/// # use bevy_ecs::{25/// # prelude::*,26/// # system::{SystemParam, ParamBuilder},27/// # };28/// # #[derive(Resource)]29/// # struct R;30/// #31/// # #[derive(SystemParam)]32/// # struct MyParam;33/// #34/// fn some_system(param: MyParam) {}35///36/// fn build_system(builder: impl SystemParamBuilder<MyParam>) {37/// let mut world = World::new();38/// // To build a system, create a tuple of `SystemParamBuilder`s39/// // with a builder for each parameter.40/// // Note that the builder for a system must be a tuple,41/// // even if there is only one parameter.42/// (builder,)43/// .build_state(&mut world)44/// .build_system(some_system);45/// }46///47/// fn build_closure_system_infer(builder: impl SystemParamBuilder<MyParam>) {48/// let mut world = World::new();49/// // Closures can be used in addition to named functions.50/// // If a closure is used, the parameter types must all be inferred51/// // from the builders, so you cannot use plain `ParamBuilder`.52/// (builder, ParamBuilder::resource())53/// .build_state(&mut world)54/// .build_system(|param, res| {55/// let param: MyParam = param;56/// let res: Res<R> = res;57/// });58/// }59///60/// fn build_closure_system_explicit(builder: impl SystemParamBuilder<MyParam>) {61/// let mut world = World::new();62/// // Alternately, you can provide all types in the closure63/// // parameter list and call `build_any_system()`.64/// (builder, ParamBuilder)65/// .build_state(&mut world)66/// .build_any_system(|param: MyParam, res: Res<R>| {});67/// }68/// ```69///70/// See the documentation for individual builders for more examples.71///72/// # List of Builders73///74/// [`ParamBuilder`] can be used for parameters that don't require any special building.75/// Using a `ParamBuilder` will build the system parameter the same way it would be initialized in an ordinary system.76///77/// `ParamBuilder` also provides factory methods that return a `ParamBuilder` typed as `impl SystemParamBuilder<P>`78/// for common system parameters that can be used to guide closure parameter inference.79///80/// [`QueryParamBuilder`] can build a [`Query`] to add additional filters,81/// or to configure the components available to [`FilteredEntityRef`](crate::world::FilteredEntityRef) or [`FilteredEntityMut`](crate::world::FilteredEntityMut).82/// You can also use a [`QueryState`] to build a [`Query`].83///84/// [`LocalBuilder`] can build a [`Local`] to supply the initial value for the `Local`.85///86/// [`FilteredResourcesParamBuilder`] can build a [`FilteredResources`],87/// and [`FilteredResourcesMutParamBuilder`] can build a [`FilteredResourcesMut`],88/// to configure the resources that can be accessed.89///90/// [`DynParamBuilder`] can build a [`DynSystemParam`] to determine the type of the inner parameter,91/// and to supply any `SystemParamBuilder` it needs.92///93/// Tuples of builders can build tuples of parameters, one builder for each element.94/// Note that since systems require a tuple as a parameter, the outer builder for a system will always be a tuple.95///96/// A [`Vec`] of builders can build a `Vec` of parameters, one builder for each element.97///98/// A [`ParamSetBuilder`] can build a [`ParamSet`].99/// This can wrap either a tuple or a `Vec`, one builder for each element.100///101/// A custom system param created with `#[derive(SystemParam)]` can be buildable if it includes a `#[system_param(builder)]` attribute.102/// See [the documentation for `SystemParam` derives](SystemParam#builders).103///104/// # Safety105///106/// The implementor must ensure that the state returned107/// from [`SystemParamBuilder::build`] is valid for `P`.108/// Note that the exact safety requirements depend on the implementation of [`SystemParam`],109/// so if `Self` is not a local type then you must call [`SystemParam::init_state`]110/// or another [`SystemParamBuilder::build`].111pub unsafe trait SystemParamBuilder<P: SystemParam>: Sized {112/// Registers any [`World`] access used by this [`SystemParam`]113/// and creates a new instance of this param's [`State`](SystemParam::State).114fn build(self, world: &mut World) -> P::State;115116/// Create a [`SystemState`] from a [`SystemParamBuilder`].117/// To create a system, call [`SystemState::build_system`] on the result.118fn build_state(self, world: &mut World) -> SystemState<P> {119SystemState::from_builder(world, self)120}121}122123/// A [`SystemParamBuilder`] for any [`SystemParam`] that uses its default initialization.124///125/// ## Example126///127/// ```128/// # use bevy_ecs::{129/// # prelude::*,130/// # system::{SystemParam, ParamBuilder},131/// # };132/// #133/// # #[derive(Component)]134/// # struct A;135/// #136/// # #[derive(Resource)]137/// # struct R;138/// #139/// # #[derive(SystemParam)]140/// # struct MyParam;141/// #142/// # let mut world = World::new();143/// # world.insert_resource(R);144/// #145/// fn my_system(res: Res<R>, param: MyParam, query: Query<&A>) {146/// // ...147/// }148///149/// let system = (150/// // A plain ParamBuilder can build any parameter type.151/// ParamBuilder,152/// // The `of::<P>()` method returns a `ParamBuilder`153/// // typed as `impl SystemParamBuilder<P>`.154/// ParamBuilder::of::<MyParam>(),155/// // The other factory methods return typed builders156/// // for common parameter types.157/// ParamBuilder::query::<&A>(),158/// )159/// .build_state(&mut world)160/// .build_system(my_system);161/// ```162#[derive(Default, Debug, Clone)]163pub struct ParamBuilder;164165// SAFETY: Calls `SystemParam::init_state`166unsafe impl<P: SystemParam> SystemParamBuilder<P> for ParamBuilder {167fn build(self, world: &mut World) -> P::State {168P::init_state(world)169}170}171172impl ParamBuilder {173/// Creates a [`SystemParamBuilder`] for any [`SystemParam`] that uses its default initialization.174pub fn of<T: SystemParam>() -> impl SystemParamBuilder<T> {175Self176}177178/// Helper method for reading a [`Resource`] as a param, equivalent to `of::<Res<T>>()`179pub fn resource<'w, T: Resource>() -> impl SystemParamBuilder<Res<'w, T>> {180Self181}182183/// Helper method for mutably accessing a [`Resource`] as a param, equivalent to `of::<ResMut<T>>()`184pub fn resource_mut<'w, T: Resource>() -> impl SystemParamBuilder<ResMut<'w, T>> {185Self186}187188/// Helper method for adding a [`Local`] as a param, equivalent to `of::<Local<T>>()`189pub fn local<'s, T: FromWorld + Send + 'static>() -> impl SystemParamBuilder<Local<'s, T>> {190Self191}192193/// Helper method for adding a [`Query`] as a param, equivalent to `of::<Query<D>>()`194pub fn query<'w, 's, D: QueryData + 'static>() -> impl SystemParamBuilder<Query<'w, 's, D, ()>>195{196Self197}198199/// Helper method for adding a filtered [`Query`] as a param, equivalent to `of::<Query<D, F>>()`200pub fn query_filtered<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>(201) -> impl SystemParamBuilder<Query<'w, 's, D, F>> {202Self203}204}205206// SAFETY: Any `QueryState<D, F>` for the correct world is valid for `Query::State`,207// and we check the world during `build`.208unsafe impl<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>209SystemParamBuilder<Query<'w, 's, D, F>> for QueryState<D, F>210{211fn build(self, world: &mut World) -> QueryState<D, F> {212self.validate_world(world.id());213self214}215}216217/// A [`SystemParamBuilder`] for a [`Query`].218/// This takes a closure accepting an `&mut` [`QueryBuilder`] and uses the builder to construct the query's state.219/// This can be used to add additional filters,220/// or to configure the components available to [`FilteredEntityRef`](crate::world::FilteredEntityRef) or [`FilteredEntityMut`](crate::world::FilteredEntityMut).221///222/// ## Example223///224/// ```225/// # use bevy_ecs::{226/// # prelude::*,227/// # system::{SystemParam, QueryParamBuilder},228/// # };229/// #230/// # #[derive(Component)]231/// # struct Player;232/// #233/// # let mut world = World::new();234/// let system = (QueryParamBuilder::new(|builder| {235/// builder.with::<Player>();236/// }),)237/// .build_state(&mut world)238/// .build_system(|query: Query<()>| {239/// for _ in &query {240/// // This only includes entities with a `Player` component.241/// }242/// });243///244/// // When collecting multiple builders into a `Vec`,245/// // use `new_box()` to erase the closure type.246/// let system = (vec![247/// QueryParamBuilder::new_box(|builder| {248/// builder.with::<Player>();249/// }),250/// QueryParamBuilder::new_box(|builder| {251/// builder.without::<Player>();252/// }),253/// ],)254/// .build_state(&mut world)255/// .build_system(|query: Vec<Query<()>>| {});256/// ```257#[derive(Clone)]258pub struct QueryParamBuilder<T>(T);259260impl<T> QueryParamBuilder<T> {261/// Creates a [`SystemParamBuilder`] for a [`Query`] that accepts a callback to configure the [`QueryBuilder`].262pub fn new<D: QueryData, F: QueryFilter>(f: T) -> Self263where264T: FnOnce(&mut QueryBuilder<D, F>),265{266Self(f)267}268}269270impl<'a, D: QueryData, F: QueryFilter>271QueryParamBuilder<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`.275pub fn new_box(f: impl FnOnce(&mut QueryBuilder<D, F>) + 'a) -> Self {276Self(Box::new(f))277}278}279280// SAFETY: Any `QueryState<D, F>` for the correct world is valid for `Query::State`,281// and `QueryBuilder` produces one with the given `world`.282unsafe impl<283'w,284's,285D: QueryData + 'static,286F: QueryFilter + 'static,287T: FnOnce(&mut QueryBuilder<D, F>),288> SystemParamBuilder<Query<'w, 's, D, F>> for QueryParamBuilder<T>289{290fn build(self, world: &mut World) -> QueryState<D, F> {291let mut builder = QueryBuilder::new(world);292(self.0)(&mut builder);293builder.build()294}295}296297macro_rules! impl_system_param_builder_tuple {298($(#[$meta:meta])* $(($param: ident, $builder: ident)),*) => {299#[expect(300clippy::allow_attributes,301reason = "This is in a macro; as such, the below lints may not always apply."302)]303#[allow(304unused_variables,305reason = "Zero-length tuples won't use any of the parameters."306)]307#[allow(308non_snake_case,309reason = "The variable names are provided by the macro caller, not by us."310)]311$(#[$meta])*312// SAFETY: implementors of each `SystemParamBuilder` in the tuple have validated their impls313unsafe impl<$($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<($($param,)*)> for ($($builder,)*) {314fn build(self, world: &mut World) -> <($($param,)*) as SystemParam>::State {315let ($($builder,)*) = self;316#[allow(317clippy::unused_unit,318reason = "Zero-length tuples won't generate any calls to the system parameter builders."319)]320($($builder.build(world),)*)321}322}323};324}325326all_tuples!(327#[doc(fake_variadic)]328impl_system_param_builder_tuple,3290,33016,331P,332B333);334335// SAFETY: implementors of each `SystemParamBuilder` in the vec have validated their impls336unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<Vec<P>> for Vec<B> {337fn build(self, world: &mut World) -> <Vec<P> as SystemParam>::State {338self.into_iter()339.map(|builder| builder.build(world))340.collect()341}342}343344/// A [`SystemParamBuilder`] for a [`ParamSet`].345///346/// To build a [`ParamSet`] with a tuple of system parameters, pass a tuple of matching [`SystemParamBuilder`]s.347/// To build a [`ParamSet`] with a [`Vec`] of system parameters, pass a `Vec` of matching [`SystemParamBuilder`]s.348///349/// # Examples350///351/// ```352/// # use bevy_ecs::{prelude::*, system::*};353/// #354/// # #[derive(Component)]355/// # struct Health;356/// #357/// # #[derive(Component)]358/// # struct Enemy;359/// #360/// # #[derive(Component)]361/// # struct Ally;362/// #363/// # let mut world = World::new();364/// #365/// let system = (ParamSetBuilder((366/// QueryParamBuilder::new(|builder| {367/// builder.with::<Enemy>();368/// }),369/// QueryParamBuilder::new(|builder| {370/// builder.with::<Ally>();371/// }),372/// ParamBuilder,373/// )),)374/// .build_state(&mut world)375/// .build_system(buildable_system_with_tuple);376/// # world.run_system_once(system);377///378/// fn buildable_system_with_tuple(379/// mut set: ParamSet<(Query<&mut Health>, Query<&mut Health>, &World)>,380/// ) {381/// // The first parameter is built from the first builder,382/// // so this will iterate over enemies.383/// for mut health in set.p0().iter_mut() {}384/// // And the second parameter is built from the second builder,385/// // so this will iterate over allies.386/// for mut health in set.p1().iter_mut() {}387/// // Parameters that don't need special building can use `ParamBuilder`.388/// let entities = set.p2().entities();389/// }390///391/// let system = (ParamSetBuilder(vec![392/// QueryParamBuilder::new_box(|builder| {393/// builder.with::<Enemy>();394/// }),395/// QueryParamBuilder::new_box(|builder| {396/// builder.with::<Ally>();397/// }),398/// ]),)399/// .build_state(&mut world)400/// .build_system(buildable_system_with_vec);401/// # world.run_system_once(system);402///403/// fn buildable_system_with_vec(mut set: ParamSet<Vec<Query<&mut Health>>>) {404/// // As with tuples, the first parameter is built from the first builder,405/// // so this will iterate over enemies.406/// for mut health in set.get_mut(0).iter_mut() {}407/// // And the second parameter is built from the second builder,408/// // so this will iterate over allies.409/// for mut health in set.get_mut(1).iter_mut() {}410/// // You can iterate over the parameters either by index,411/// // or using the `for_each` method.412/// set.for_each(|mut query| for mut health in query.iter_mut() {});413/// }414/// ```415#[derive(Debug, Default, Clone)]416pub struct ParamSetBuilder<T>(pub T);417418macro_rules! impl_param_set_builder_tuple {419($(($param: ident, $builder: ident)),*) => {420#[expect(421clippy::allow_attributes,422reason = "This is in a macro; as such, the below lints may not always apply."423)]424#[allow(425unused_variables,426reason = "Zero-length tuples won't use any of the parameters."427)]428#[allow(429non_snake_case,430reason = "The variable names are provided by the macro caller, not by us."431)]432// SAFETY: implementors of each `SystemParamBuilder` in the tuple have validated their impls433unsafe impl<'w, 's, $($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<ParamSet<'w, 's, ($($param,)*)>> for ParamSetBuilder<($($builder,)*)> {434fn build(self, world: &mut World) -> <($($param,)*) as SystemParam>::State {435let ParamSetBuilder(($($builder,)*)) = self;436($($builder.build(world),)*)437}438}439};440}441442all_tuples!(impl_param_set_builder_tuple, 1, 8, P, B);443444// SAFETY: implementors of each `SystemParamBuilder` in the vec have validated their impls445unsafe impl<'w, 's, P: SystemParam, B: SystemParamBuilder<P>>446SystemParamBuilder<ParamSet<'w, 's, Vec<P>>> for ParamSetBuilder<Vec<B>>447{448fn build(self, world: &mut World) -> <Vec<P> as SystemParam>::State {449self.0450.into_iter()451.map(|builder| builder.build(world))452.collect()453}454}455456/// A [`SystemParamBuilder`] for a [`DynSystemParam`].457/// See the [`DynSystemParam`] docs for examples.458pub struct DynParamBuilder<'a>(Box<dyn FnOnce(&mut World) -> DynSystemParamState + 'a>);459460impl<'a> DynParamBuilder<'a> {461/// Creates a new [`DynParamBuilder`] by wrapping a [`SystemParamBuilder`] of any type.462/// The built [`DynSystemParam`] can be downcast to `T`.463pub fn new<T: SystemParam + 'static>(builder: impl SystemParamBuilder<T> + 'a) -> Self {464Self(Box::new(|world| {465DynSystemParamState::new::<T>(builder.build(world))466}))467}468}469470// SAFETY: `DynSystemParam::get_param` will call `get_param` on the boxed `DynSystemParamState`,471// and the boxed builder was a valid implementation of `SystemParamBuilder` for that type.472// The resulting `DynSystemParam` can only perform access by downcasting to that param type.473unsafe impl<'a, 'w, 's> SystemParamBuilder<DynSystemParam<'w, 's>> for DynParamBuilder<'a> {474fn build(self, world: &mut World) -> <DynSystemParam<'w, 's> as SystemParam>::State {475(self.0)(world)476}477}478479/// A [`SystemParamBuilder`] for a [`Local`].480/// The provided value will be used as the initial value of the `Local`.481///482/// ## Example483///484/// ```485/// # use bevy_ecs::{486/// # prelude::*,487/// # system::{SystemParam, LocalBuilder, RunSystemOnce},488/// # };489/// #490/// # let mut world = World::new();491/// let system = (LocalBuilder(100),)492/// .build_state(&mut world)493/// .build_system(|local: Local<usize>| {494/// assert_eq!(*local, 100);495/// });496/// # world.run_system_once(system);497/// ```498#[derive(Default, Debug, Clone)]499pub struct LocalBuilder<T>(pub T);500501// SAFETY: Any value of `T` is a valid state for `Local`.502unsafe impl<'s, T: FromWorld + Send + 'static> SystemParamBuilder<Local<'s, T>>503for LocalBuilder<T>504{505fn build(self, _world: &mut World) -> <Local<'s, T> as SystemParam>::State {506SyncCell::new(self.0)507}508}509510/// A [`SystemParamBuilder`] for a [`FilteredResources`].511/// See the [`FilteredResources`] docs for examples.512#[derive(Clone)]513pub struct FilteredResourcesParamBuilder<T>(T);514515impl<T> FilteredResourcesParamBuilder<T> {516/// Creates a [`SystemParamBuilder`] for a [`FilteredResources`] that accepts a callback to configure the [`FilteredResourcesBuilder`].517pub fn new(f: T) -> Self518where519T: FnOnce(&mut FilteredResourcesBuilder),520{521Self(f)522}523}524525impl<'a> FilteredResourcesParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesBuilder) + 'a>> {526/// Creates a [`SystemParamBuilder`] for a [`FilteredResources`] that accepts a callback to configure the [`FilteredResourcesBuilder`].527/// This boxes the callback so that it has a common type.528pub fn new_box(f: impl FnOnce(&mut FilteredResourcesBuilder) + 'a) -> Self {529Self(Box::new(f))530}531}532533// SAFETY: Any `Access` is a valid state for `FilteredResources`.534unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesBuilder)>535SystemParamBuilder<FilteredResources<'w, 's>> for FilteredResourcesParamBuilder<T>536{537fn build(self, world: &mut World) -> <FilteredResources<'w, 's> as SystemParam>::State {538let mut builder = FilteredResourcesBuilder::new(world);539(self.0)(&mut builder);540builder.build()541}542}543544/// A [`SystemParamBuilder`] for a [`FilteredResourcesMut`].545/// See the [`FilteredResourcesMut`] docs for examples.546#[derive(Clone)]547pub struct FilteredResourcesMutParamBuilder<T>(T);548549impl<T> FilteredResourcesMutParamBuilder<T> {550/// Creates a [`SystemParamBuilder`] for a [`FilteredResourcesMut`] that accepts a callback to configure the [`FilteredResourcesMutBuilder`].551pub fn new(f: T) -> Self552where553T: FnOnce(&mut FilteredResourcesMutBuilder),554{555Self(f)556}557}558559impl<'a> FilteredResourcesMutParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesMutBuilder) + 'a>> {560/// Creates a [`SystemParamBuilder`] for a [`FilteredResourcesMut`] that accepts a callback to configure the [`FilteredResourcesMutBuilder`].561/// This boxes the callback so that it has a common type.562pub fn new_box(f: impl FnOnce(&mut FilteredResourcesMutBuilder) + 'a) -> Self {563Self(Box::new(f))564}565}566567// SAFETY: Any `Access` is a valid state for `FilteredResourcesMut`.568unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesMutBuilder)>569SystemParamBuilder<FilteredResourcesMut<'w, 's>> for FilteredResourcesMutParamBuilder<T>570{571fn build(self, world: &mut World) -> <FilteredResourcesMut<'w, 's> as SystemParam>::State {572let mut builder = FilteredResourcesMutBuilder::new(world);573(self.0)(&mut builder);574builder.build()575}576}577578/// A [`SystemParamBuilder`] for an [`Option`].579#[derive(Clone)]580pub struct OptionBuilder<T>(T);581582// SAFETY: `OptionBuilder<B>` builds a state that is valid for `P`, and any state valid for `P` is valid for `Option<P>`583unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<Option<P>>584for OptionBuilder<B>585{586fn build(self, world: &mut World) -> <Option<P> as SystemParam>::State {587self.0.build(world)588}589}590591/// A [`SystemParamBuilder`] for a [`Result`] of [`SystemParamValidationError`].592#[derive(Clone)]593pub struct ResultBuilder<T>(T);594595// SAFETY: `ResultBuilder<B>` builds a state that is valid for `P`, and any state valid for `P` is valid for `Result<P, SystemParamValidationError>`596unsafe impl<P: SystemParam, B: SystemParamBuilder<P>>597SystemParamBuilder<Result<P, SystemParamValidationError>> for ResultBuilder<B>598{599fn build(600self,601world: &mut World,602) -> <Result<P, SystemParamValidationError> as SystemParam>::State {603self.0.build(world)604}605}606607/// A [`SystemParamBuilder`] for a [`If`].608#[derive(Clone)]609pub struct IfBuilder<T>(T);610611// SAFETY: `IfBuilder<B>` builds a state that is valid for `P`, and any state valid for `P` is valid for `If<P>`612unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<If<P>> for IfBuilder<B> {613fn build(self, world: &mut World) -> <If<P> as SystemParam>::State {614self.0.build(world)615}616}617618#[cfg(test)]619mod tests {620use crate::{621entity::Entities,622error::Result,623prelude::{Component, Query},624reflect::ReflectResource,625system::{Local, RunSystemOnce},626};627use alloc::vec;628use bevy_reflect::{FromType, Reflect, ReflectRef};629630use super::*;631632#[derive(Component)]633struct A;634635#[derive(Component)]636struct B;637638#[derive(Component)]639struct C;640641#[derive(Resource, Default, Reflect)]642#[reflect(Resource)]643struct R {644foo: usize,645}646647fn local_system(local: Local<u64>) -> u64 {648*local649}650651fn query_system(query: Query<()>) -> usize {652query.iter().count()653}654655fn query_system_result(query: Query<()>) -> Result<usize> {656Ok(query.iter().count())657}658659fn multi_param_system(a: Local<u64>, b: Local<u64>) -> u64 {660*a + *b + 1661}662663#[test]664fn local_builder() {665let mut world = World::new();666667let system = (LocalBuilder(10),)668.build_state(&mut world)669.build_system(local_system);670671let output = world.run_system_once(system).unwrap();672assert_eq!(output, 10);673}674675#[test]676fn query_builder() {677let mut world = World::new();678679world.spawn(A);680world.spawn_empty();681682let system = (QueryParamBuilder::new(|query| {683query.with::<A>();684}),)685.build_state(&mut world)686.build_system(query_system);687688let output = world.run_system_once(system).unwrap();689assert_eq!(output, 1);690}691692#[test]693fn query_builder_result_fallible() {694let mut world = World::new();695696world.spawn(A);697world.spawn_empty();698699let system = (QueryParamBuilder::new(|query| {700query.with::<A>();701}),)702.build_state(&mut world)703.build_system(query_system_result);704705// The type annotation here is necessary since the system706// could also return `Result<usize>`707let output: usize = world.run_system_once(system).unwrap();708assert_eq!(output, 1);709}710711#[test]712fn query_builder_result_infallible() {713let mut world = World::new();714715world.spawn(A);716world.spawn_empty();717718let system = (QueryParamBuilder::new(|query| {719query.with::<A>();720}),)721.build_state(&mut world)722.build_system(query_system_result);723724// The type annotation here is necessary since the system725// could also return `usize`726let output: Result<usize> = world.run_system_once(system).unwrap();727assert_eq!(output.unwrap(), 1);728}729730#[test]731fn query_builder_state() {732let mut world = World::new();733734world.spawn(A);735world.spawn_empty();736737let state = QueryBuilder::new(&mut world).with::<A>().build();738739let system = (state,).build_state(&mut world).build_system(query_system);740741let output = world.run_system_once(system).unwrap();742assert_eq!(output, 1);743}744745#[test]746fn multi_param_builder() {747let mut world = World::new();748749world.spawn(A);750world.spawn_empty();751752let system = (LocalBuilder(0), ParamBuilder)753.build_state(&mut world)754.build_system(multi_param_system);755756let output = world.run_system_once(system).unwrap();757assert_eq!(output, 1);758}759760#[test]761fn vec_builder() {762let mut world = World::new();763764world.spawn((A, B, C));765world.spawn((A, B));766world.spawn((A, C));767world.spawn((A, C));768world.spawn_empty();769770let system = (vec![771QueryParamBuilder::new_box(|builder| {772builder.with::<B>().without::<C>();773}),774QueryParamBuilder::new_box(|builder| {775builder.with::<C>().without::<B>();776}),777],)778.build_state(&mut world)779.build_system(|params: Vec<Query<&mut A>>| {780let mut count: usize = 0;781params782.into_iter()783.for_each(|mut query| count += query.iter_mut().count());784count785});786787let output = world.run_system_once(system).unwrap();788assert_eq!(output, 3);789}790791#[test]792fn multi_param_builder_inference() {793let mut world = World::new();794795world.spawn(A);796world.spawn_empty();797798let system = (LocalBuilder(0u64), ParamBuilder::local::<u64>())799.build_state(&mut world)800.build_system(|a, b| *a + *b + 1);801802let output = world.run_system_once(system).unwrap();803assert_eq!(output, 1);804}805806#[test]807fn param_set_builder() {808let mut world = World::new();809810world.spawn((A, B, C));811world.spawn((A, B));812world.spawn((A, C));813world.spawn((A, C));814world.spawn_empty();815816let system = (ParamSetBuilder((817QueryParamBuilder::new(|builder| {818builder.with::<B>();819}),820QueryParamBuilder::new(|builder| {821builder.with::<C>();822}),823)),)824.build_state(&mut world)825.build_system(|mut params: ParamSet<(Query<&mut A>, Query<&mut A>)>| {826params.p0().iter().count() + params.p1().iter().count()827});828829let output = world.run_system_once(system).unwrap();830assert_eq!(output, 5);831}832833#[test]834fn param_set_vec_builder() {835let mut world = World::new();836837world.spawn((A, B, C));838world.spawn((A, B));839world.spawn((A, C));840world.spawn((A, C));841world.spawn_empty();842843let system = (ParamSetBuilder(vec![844QueryParamBuilder::new_box(|builder| {845builder.with::<B>();846}),847QueryParamBuilder::new_box(|builder| {848builder.with::<C>();849}),850]),)851.build_state(&mut world)852.build_system(|mut params: ParamSet<Vec<Query<&mut A>>>| {853let mut count = 0;854params.for_each(|mut query| count += query.iter_mut().count());855count856});857858let output = world.run_system_once(system).unwrap();859assert_eq!(output, 5);860}861862#[test]863fn dyn_builder() {864let mut world = World::new();865866world.spawn(A);867world.spawn_empty();868869let system = (870DynParamBuilder::new(LocalBuilder(3_usize)),871DynParamBuilder::new::<Query<()>>(QueryParamBuilder::new(|builder| {872builder.with::<A>();873})),874DynParamBuilder::new::<&Entities>(ParamBuilder),875)876.build_state(&mut world)877.build_system(878|mut p0: DynSystemParam, mut p1: DynSystemParam, mut p2: DynSystemParam| {879let local = *p0.downcast_mut::<Local<usize>>().unwrap();880let query_count = p1.downcast_mut::<Query<()>>().unwrap().iter().count();881let _entities = p2.downcast_mut::<&Entities>().unwrap();882assert!(p0.downcast_mut::<Query<()>>().is_none());883local + query_count884},885);886887let output = world.run_system_once(system).unwrap();888assert_eq!(output, 4);889}890891#[derive(SystemParam)]892#[system_param(builder)]893struct CustomParam<'w, 's> {894query: Query<'w, 's, ()>,895local: Local<'s, usize>,896}897898#[test]899fn custom_param_builder() {900let mut world = World::new();901902world.spawn(A);903world.spawn_empty();904905let system = (CustomParamBuilder {906local: LocalBuilder(100),907query: QueryParamBuilder::new(|builder| {908builder.with::<A>();909}),910},)911.build_state(&mut world)912.build_system(|param: CustomParam| *param.local + param.query.iter().count());913914let output = world.run_system_once(system).unwrap();915assert_eq!(output, 101);916}917918#[test]919fn filtered_resource_conflicts_read_with_res() {920let mut world = World::new();921(922ParamBuilder::resource(),923FilteredResourcesParamBuilder::new(|builder| {924builder.add_read::<R>();925}),926)927.build_state(&mut world)928.build_system(|_r: Res<R>, _fr: FilteredResources| {});929}930931#[test]932#[should_panic]933fn filtered_resource_conflicts_read_with_resmut() {934let mut world = World::new();935(936ParamBuilder::resource_mut(),937FilteredResourcesParamBuilder::new(|builder| {938builder.add_read::<R>();939}),940)941.build_state(&mut world)942.build_system(|_r: ResMut<R>, _fr: FilteredResources| {});943}944945#[test]946#[should_panic]947fn filtered_resource_conflicts_read_all_with_resmut() {948let mut world = World::new();949(950ParamBuilder::resource_mut(),951FilteredResourcesParamBuilder::new(|builder| {952builder.add_read_all();953}),954)955.build_state(&mut world)956.build_system(|_r: ResMut<R>, _fr: FilteredResources| {});957}958959#[test]960fn filtered_resource_mut_conflicts_read_with_res() {961let mut world = World::new();962(963ParamBuilder::resource(),964FilteredResourcesMutParamBuilder::new(|builder| {965builder.add_read::<R>();966}),967)968.build_state(&mut world)969.build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});970}971972#[test]973#[should_panic]974fn filtered_resource_mut_conflicts_read_with_resmut() {975let mut world = World::new();976(977ParamBuilder::resource_mut(),978FilteredResourcesMutParamBuilder::new(|builder| {979builder.add_read::<R>();980}),981)982.build_state(&mut world)983.build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});984}985986#[test]987#[should_panic]988fn filtered_resource_mut_conflicts_write_with_res() {989let mut world = World::new();990(991ParamBuilder::resource(),992FilteredResourcesMutParamBuilder::new(|builder| {993builder.add_write::<R>();994}),995)996.build_state(&mut world)997.build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});998}9991000#[test]1001#[should_panic]1002fn filtered_resource_mut_conflicts_write_all_with_res() {1003let mut world = World::new();1004(1005ParamBuilder::resource(),1006FilteredResourcesMutParamBuilder::new(|builder| {1007builder.add_write_all();1008}),1009)1010.build_state(&mut world)1011.build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});1012}10131014#[test]1015#[should_panic]1016fn filtered_resource_mut_conflicts_write_with_resmut() {1017let mut world = World::new();1018(1019ParamBuilder::resource_mut(),1020FilteredResourcesMutParamBuilder::new(|builder| {1021builder.add_write::<R>();1022}),1023)1024.build_state(&mut world)1025.build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});1026}10271028#[test]1029fn filtered_resource_reflect() {1030let mut world = World::new();1031world.insert_resource(R { foo: 7 });10321033let system = (FilteredResourcesParamBuilder::new(|builder| {1034builder.add_read::<R>();1035}),)1036.build_state(&mut world)1037.build_system(|res: FilteredResources| {1038let reflect_resource = <ReflectResource as FromType<R>>::from_type();1039let ReflectRef::Struct(reflect_struct) =1040reflect_resource.reflect(res).unwrap().reflect_ref()1041else {1042panic!()1043};1044*reflect_struct1045.field("foo")1046.unwrap()1047.try_downcast_ref::<usize>()1048.unwrap()1049});10501051let output = world.run_system_once(system).unwrap();1052assert_eq!(output, 7);1053}1054}105510561057