1use alloc::{boxed::Box, vec::Vec};
2use bevy_utils::synccell::SyncCell;
3use variadics_please::all_tuples;
4
5use crate::{
6 prelude::QueryBuilder,
7 query::{QueryData, QueryFilter, QueryState},
8 resource::Resource,
9 system::{
10 DynSystemParam, DynSystemParamState, Local, ParamSet, Query, SystemMeta, SystemParam,
11 },
12 world::{
13 FilteredResources, FilteredResourcesBuilder, FilteredResourcesMut,
14 FilteredResourcesMutBuilder, FromWorld, World,
15 },
16};
17use core::fmt::Debug;
18
19use super::{init_query_param, Res, ResMut, SystemState};
20
21pub unsafe trait SystemParamBuilder<P: SystemParam>: Sized {
116 fn build(self, world: &mut World, meta: &mut SystemMeta) -> P::State;
119
120 fn build_state(self, world: &mut World) -> SystemState<P> {
123 SystemState::from_builder(world, self)
124 }
125}
126
127#[derive(Default, Debug, Clone)]
167pub struct ParamBuilder;
168
169unsafe impl<P: SystemParam> SystemParamBuilder<P> for ParamBuilder {
171 fn build(self, world: &mut World, meta: &mut SystemMeta) -> P::State {
172 P::init_state(world, meta)
173 }
174}
175
176impl ParamBuilder {
177 pub fn of<T: SystemParam>() -> impl SystemParamBuilder<T> {
179 Self
180 }
181
182 pub fn resource<'w, T: Resource>() -> impl SystemParamBuilder<Res<'w, T>> {
184 Self
185 }
186
187 pub fn resource_mut<'w, T: Resource>() -> impl SystemParamBuilder<ResMut<'w, T>> {
189 Self
190 }
191
192 pub fn local<'s, T: FromWorld + Send + 'static>() -> impl SystemParamBuilder<Local<'s, T>> {
194 Self
195 }
196
197 pub fn query<'w, 's, D: QueryData + 'static>() -> impl SystemParamBuilder<Query<'w, 's, D, ()>>
199 {
200 Self
201 }
202
203 pub fn query_filtered<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>(
205 ) -> impl SystemParamBuilder<Query<'w, 's, D, F>> {
206 Self
207 }
208}
209
210unsafe impl<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>
212 SystemParamBuilder<Query<'w, 's, D, F>> for QueryState<D, F>
213{
214 fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> QueryState<D, F> {
215 self.validate_world(world.id());
216 init_query_param(world, system_meta, &self);
217 self
218 }
219}
220
221#[derive(Clone)]
262pub struct QueryParamBuilder<T>(T);
263
264impl<T> QueryParamBuilder<T> {
265 pub fn new<D: QueryData, F: QueryFilter>(f: T) -> Self
267 where
268 T: FnOnce(&mut QueryBuilder<D, F>),
269 {
270 Self(f)
271 }
272}
273
274impl<'a, D: QueryData, F: QueryFilter>
275 QueryParamBuilder<Box<dyn FnOnce(&mut QueryBuilder<D, F>) + 'a>>
276{
277 pub fn new_box(f: impl FnOnce(&mut QueryBuilder<D, F>) + 'a) -> Self {
280 Self(Box::new(f))
281 }
282}
283
284unsafe impl<
286 'w,
287 's,
288 D: QueryData + 'static,
289 F: QueryFilter + 'static,
290 T: FnOnce(&mut QueryBuilder<D, F>),
291 > SystemParamBuilder<Query<'w, 's, D, F>> for QueryParamBuilder<T>
292{
293 fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> QueryState<D, F> {
294 let mut builder = QueryBuilder::new(world);
295 (self.0)(&mut builder);
296 let state = builder.build();
297 init_query_param(world, system_meta, &state);
298 state
299 }
300}
301
302macro_rules! impl_system_param_builder_tuple {
303 ($(#[$meta:meta])* $(($param: ident, $builder: ident)),*) => {
304 #[expect(
305 clippy::allow_attributes,
306 reason = "This is in a macro; as such, the below lints may not always apply."
307 )]
308 #[allow(
309 unused_variables,
310 reason = "Zero-length tuples won't use any of the parameters."
311 )]
312 #[allow(
313 non_snake_case,
314 reason = "The variable names are provided by the macro caller, not by us."
315 )]
316 $(#[$meta])*
317 unsafe impl<$($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<($($param,)*)> for ($($builder,)*) {
319 fn build(self, world: &mut World, meta: &mut SystemMeta) -> <($($param,)*) as SystemParam>::State {
320 let ($($builder,)*) = self;
321 #[allow(
322 clippy::unused_unit,
323 reason = "Zero-length tuples won't generate any calls to the system parameter builders."
324 )]
325 ($($builder.build(world, meta),)*)
326 }
327 }
328 };
329}
330
331all_tuples!(
332 #[doc(fake_variadic)]
333 impl_system_param_builder_tuple,
334 0,
335 16,
336 P,
337 B
338);
339
340unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<Vec<P>> for Vec<B> {
342 fn build(self, world: &mut World, meta: &mut SystemMeta) -> <Vec<P> as SystemParam>::State {
343 self.into_iter()
344 .map(|builder| builder.build(world, meta))
345 .collect()
346 }
347}
348
349#[derive(Debug, Default, Clone)]
421pub struct ParamSetBuilder<T>(pub T);
422
423macro_rules! impl_param_set_builder_tuple {
424 ($(($param: ident, $builder: ident, $meta: ident)),*) => {
425 #[expect(
426 clippy::allow_attributes,
427 reason = "This is in a macro; as such, the below lints may not always apply."
428 )]
429 #[allow(
430 unused_variables,
431 reason = "Zero-length tuples won't use any of the parameters."
432 )]
433 #[allow(
434 non_snake_case,
435 reason = "The variable names are provided by the macro caller, not by us."
436 )]
437 unsafe impl<'w, 's, $($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<ParamSet<'w, 's, ($($param,)*)>> for ParamSetBuilder<($($builder,)*)> {
439 fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> <($($param,)*) as SystemParam>::State {
440 let ParamSetBuilder(($($builder,)*)) = self;
441 $(
447 let mut $meta = system_meta.clone();
448 let $param = $builder.build(world, &mut $meta);
449 )*
450 if false $(|| !$meta.is_send())* {
452 system_meta.set_non_send();
453 }
454 $(
455 system_meta
456 .component_access_set
457 .extend($meta.component_access_set);
458 system_meta
459 .archetype_component_access
460 .extend(&$meta.archetype_component_access);
461 )*
462 #[allow(
463 clippy::unused_unit,
464 reason = "Zero-length tuples won't generate any calls to the system parameter builders."
465 )]
466 ($($param,)*)
467 }
468 }
469 };
470}
471
472all_tuples!(impl_param_set_builder_tuple, 1, 8, P, B, meta);
473
474unsafe impl<'w, 's, P: SystemParam, B: SystemParamBuilder<P>>
477 SystemParamBuilder<ParamSet<'w, 's, Vec<P>>> for ParamSetBuilder<Vec<B>>
478{
479 fn build(
480 self,
481 world: &mut World,
482 system_meta: &mut SystemMeta,
483 ) -> <Vec<P> as SystemParam>::State {
484 let mut states = Vec::with_capacity(self.0.len());
485 let mut metas = Vec::with_capacity(self.0.len());
486 for builder in self.0 {
487 let mut meta = system_meta.clone();
488 states.push(builder.build(world, &mut meta));
489 metas.push(meta);
490 }
491 if metas.iter().any(|m| !m.is_send()) {
492 system_meta.set_non_send();
493 }
494 for meta in metas {
495 system_meta
496 .component_access_set
497 .extend(meta.component_access_set);
498 system_meta
499 .archetype_component_access
500 .extend(&meta.archetype_component_access);
501 }
502 states
503 }
504}
505
506pub struct DynParamBuilder<'a>(
509 Box<dyn FnOnce(&mut World, &mut SystemMeta) -> DynSystemParamState + 'a>,
510);
511
512impl<'a> DynParamBuilder<'a> {
513 pub fn new<T: SystemParam + 'static>(builder: impl SystemParamBuilder<T> + 'a) -> Self {
516 Self(Box::new(|world, meta| {
517 DynSystemParamState::new::<T>(builder.build(world, meta))
518 }))
519 }
520}
521
522unsafe impl<'a, 'w, 's> SystemParamBuilder<DynSystemParam<'w, 's>> for DynParamBuilder<'a> {
526 fn build(
527 self,
528 world: &mut World,
529 meta: &mut SystemMeta,
530 ) -> <DynSystemParam<'w, 's> as SystemParam>::State {
531 (self.0)(world, meta)
532 }
533}
534
535#[derive(Default, Debug, Clone)]
555pub struct LocalBuilder<T>(pub T);
556
557unsafe impl<'s, T: FromWorld + Send + 'static> SystemParamBuilder<Local<'s, T>>
559 for LocalBuilder<T>
560{
561 fn build(
562 self,
563 _world: &mut World,
564 _meta: &mut SystemMeta,
565 ) -> <Local<'s, T> as SystemParam>::State {
566 SyncCell::new(self.0)
567 }
568}
569
570#[derive(Clone)]
573pub struct FilteredResourcesParamBuilder<T>(T);
574
575impl<T> FilteredResourcesParamBuilder<T> {
576 pub fn new(f: T) -> Self
578 where
579 T: FnOnce(&mut FilteredResourcesBuilder),
580 {
581 Self(f)
582 }
583}
584
585impl<'a> FilteredResourcesParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesBuilder) + 'a>> {
586 pub fn new_box(f: impl FnOnce(&mut FilteredResourcesBuilder) + 'a) -> Self {
589 Self(Box::new(f))
590 }
591}
592
593unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesBuilder)>
596 SystemParamBuilder<FilteredResources<'w, 's>> for FilteredResourcesParamBuilder<T>
597{
598 fn build(
599 self,
600 world: &mut World,
601 meta: &mut SystemMeta,
602 ) -> <FilteredResources<'w, 's> as SystemParam>::State {
603 let mut builder = FilteredResourcesBuilder::new(world);
604 (self.0)(&mut builder);
605 let access = builder.build();
606
607 let combined_access = meta.component_access_set.combined_access();
608 let conflicts = combined_access.get_conflicts(&access);
609 if !conflicts.is_empty() {
610 let accesses = conflicts.format_conflict_list(world);
611 let system_name = &meta.name;
612 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");
613 }
614
615 if access.has_read_all_resources() {
616 meta.component_access_set
617 .add_unfiltered_read_all_resources();
618 meta.archetype_component_access.read_all_resources();
619 } else {
620 for component_id in access.resource_reads_and_writes() {
621 meta.component_access_set
622 .add_unfiltered_resource_read(component_id);
623
624 let archetype_component_id = world.initialize_resource_internal(component_id).id();
625 meta.archetype_component_access
626 .add_resource_read(archetype_component_id);
627 }
628 }
629
630 access
631 }
632}
633
634#[derive(Clone)]
637pub struct FilteredResourcesMutParamBuilder<T>(T);
638
639impl<T> FilteredResourcesMutParamBuilder<T> {
640 pub fn new(f: T) -> Self
642 where
643 T: FnOnce(&mut FilteredResourcesMutBuilder),
644 {
645 Self(f)
646 }
647}
648
649impl<'a> FilteredResourcesMutParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesMutBuilder) + 'a>> {
650 pub fn new_box(f: impl FnOnce(&mut FilteredResourcesMutBuilder) + 'a) -> Self {
653 Self(Box::new(f))
654 }
655}
656
657unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesMutBuilder)>
660 SystemParamBuilder<FilteredResourcesMut<'w, 's>> for FilteredResourcesMutParamBuilder<T>
661{
662 fn build(
663 self,
664 world: &mut World,
665 meta: &mut SystemMeta,
666 ) -> <FilteredResourcesMut<'w, 's> as SystemParam>::State {
667 let mut builder = FilteredResourcesMutBuilder::new(world);
668 (self.0)(&mut builder);
669 let access = builder.build();
670
671 let combined_access = meta.component_access_set.combined_access();
672 let conflicts = combined_access.get_conflicts(&access);
673 if !conflicts.is_empty() {
674 let accesses = conflicts.format_conflict_list(world);
675 let system_name = &meta.name;
676 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");
677 }
678
679 if access.has_read_all_resources() {
680 meta.component_access_set
681 .add_unfiltered_read_all_resources();
682 meta.archetype_component_access.read_all_resources();
683 } else {
684 for component_id in access.resource_reads() {
685 meta.component_access_set
686 .add_unfiltered_resource_read(component_id);
687
688 let archetype_component_id = world.initialize_resource_internal(component_id).id();
689 meta.archetype_component_access
690 .add_resource_read(archetype_component_id);
691 }
692 }
693
694 if access.has_write_all_resources() {
695 meta.component_access_set
696 .add_unfiltered_write_all_resources();
697 meta.archetype_component_access.write_all_resources();
698 } else {
699 for component_id in access.resource_writes() {
700 meta.component_access_set
701 .add_unfiltered_resource_write(component_id);
702
703 let archetype_component_id = world.initialize_resource_internal(component_id).id();
704 meta.archetype_component_access
705 .add_resource_write(archetype_component_id);
706 }
707 }
708
709 access
710 }
711}
712
713#[cfg(test)]
714mod tests {
715 use crate::{
716 entity::Entities,
717 prelude::{Component, Query},
718 reflect::ReflectResource,
719 system::{Local, RunSystemOnce},
720 };
721 use alloc::vec;
722 use bevy_reflect::{FromType, Reflect, ReflectRef};
723
724 use super::*;
725
726 #[derive(Component)]
727 struct A;
728
729 #[derive(Component)]
730 struct B;
731
732 #[derive(Component)]
733 struct C;
734
735 #[derive(Resource, Default, Reflect)]
736 #[reflect(Resource)]
737 struct R {
738 foo: usize,
739 }
740
741 fn local_system(local: Local<u64>) -> u64 {
742 *local
743 }
744
745 fn query_system(query: Query<()>) -> usize {
746 query.iter().count()
747 }
748
749 fn multi_param_system(a: Local<u64>, b: Local<u64>) -> u64 {
750 *a + *b + 1
751 }
752
753 #[test]
754 fn local_builder() {
755 let mut world = World::new();
756
757 let system = (LocalBuilder(10),)
758 .build_state(&mut world)
759 .build_system(local_system);
760
761 let output = world.run_system_once(system).unwrap();
762 assert_eq!(output, 10);
763 }
764
765 #[test]
766 fn query_builder() {
767 let mut world = World::new();
768
769 world.spawn(A);
770 world.spawn_empty();
771
772 let system = (QueryParamBuilder::new(|query| {
773 query.with::<A>();
774 }),)
775 .build_state(&mut world)
776 .build_system(query_system);
777
778 let output = world.run_system_once(system).unwrap();
779 assert_eq!(output, 1);
780 }
781
782 #[test]
783 fn query_builder_state() {
784 let mut world = World::new();
785
786 world.spawn(A);
787 world.spawn_empty();
788
789 let state = QueryBuilder::new(&mut world).with::<A>().build();
790
791 let system = (state,).build_state(&mut world).build_system(query_system);
792
793 let output = world.run_system_once(system).unwrap();
794 assert_eq!(output, 1);
795 }
796
797 #[test]
798 fn multi_param_builder() {
799 let mut world = World::new();
800
801 world.spawn(A);
802 world.spawn_empty();
803
804 let system = (LocalBuilder(0), ParamBuilder)
805 .build_state(&mut world)
806 .build_system(multi_param_system);
807
808 let output = world.run_system_once(system).unwrap();
809 assert_eq!(output, 1);
810 }
811
812 #[test]
813 fn vec_builder() {
814 let mut world = World::new();
815
816 world.spawn((A, B, C));
817 world.spawn((A, B));
818 world.spawn((A, C));
819 world.spawn((A, C));
820 world.spawn_empty();
821
822 let system = (vec![
823 QueryParamBuilder::new_box(|builder| {
824 builder.with::<B>().without::<C>();
825 }),
826 QueryParamBuilder::new_box(|builder| {
827 builder.with::<C>().without::<B>();
828 }),
829 ],)
830 .build_state(&mut world)
831 .build_system(|params: Vec<Query<&mut A>>| {
832 let mut count: usize = 0;
833 params
834 .into_iter()
835 .for_each(|mut query| count += query.iter_mut().count());
836 count
837 });
838
839 let output = world.run_system_once(system).unwrap();
840 assert_eq!(output, 3);
841 }
842
843 #[test]
844 fn multi_param_builder_inference() {
845 let mut world = World::new();
846
847 world.spawn(A);
848 world.spawn_empty();
849
850 let system = (LocalBuilder(0u64), ParamBuilder::local::<u64>())
851 .build_state(&mut world)
852 .build_system(|a, b| *a + *b + 1);
853
854 let output = world.run_system_once(system).unwrap();
855 assert_eq!(output, 1);
856 }
857
858 #[test]
859 fn param_set_builder() {
860 let mut world = World::new();
861
862 world.spawn((A, B, C));
863 world.spawn((A, B));
864 world.spawn((A, C));
865 world.spawn((A, C));
866 world.spawn_empty();
867
868 let system = (ParamSetBuilder((
869 QueryParamBuilder::new(|builder| {
870 builder.with::<B>();
871 }),
872 QueryParamBuilder::new(|builder| {
873 builder.with::<C>();
874 }),
875 )),)
876 .build_state(&mut world)
877 .build_system(|mut params: ParamSet<(Query<&mut A>, Query<&mut A>)>| {
878 params.p0().iter().count() + params.p1().iter().count()
879 });
880
881 let output = world.run_system_once(system).unwrap();
882 assert_eq!(output, 5);
883 }
884
885 #[test]
886 fn param_set_vec_builder() {
887 let mut world = World::new();
888
889 world.spawn((A, B, C));
890 world.spawn((A, B));
891 world.spawn((A, C));
892 world.spawn((A, C));
893 world.spawn_empty();
894
895 let system = (ParamSetBuilder(vec![
896 QueryParamBuilder::new_box(|builder| {
897 builder.with::<B>();
898 }),
899 QueryParamBuilder::new_box(|builder| {
900 builder.with::<C>();
901 }),
902 ]),)
903 .build_state(&mut world)
904 .build_system(|mut params: ParamSet<Vec<Query<&mut A>>>| {
905 let mut count = 0;
906 params.for_each(|mut query| count += query.iter_mut().count());
907 count
908 });
909
910 let output = world.run_system_once(system).unwrap();
911 assert_eq!(output, 5);
912 }
913
914 #[test]
915 fn dyn_builder() {
916 let mut world = World::new();
917
918 world.spawn(A);
919 world.spawn_empty();
920
921 let system = (
922 DynParamBuilder::new(LocalBuilder(3_usize)),
923 DynParamBuilder::new::<Query<()>>(QueryParamBuilder::new(|builder| {
924 builder.with::<A>();
925 })),
926 DynParamBuilder::new::<&Entities>(ParamBuilder),
927 )
928 .build_state(&mut world)
929 .build_system(
930 |mut p0: DynSystemParam, mut p1: DynSystemParam, mut p2: DynSystemParam| {
931 let local = *p0.downcast_mut::<Local<usize>>().unwrap();
932 let query_count = p1.downcast_mut::<Query<()>>().unwrap().iter().count();
933 let _entities = p2.downcast_mut::<&Entities>().unwrap();
934 assert!(p0.downcast_mut::<Query<()>>().is_none());
935 local + query_count
936 },
937 );
938
939 let output = world.run_system_once(system).unwrap();
940 assert_eq!(output, 4);
941 }
942
943 #[derive(SystemParam)]
944 #[system_param(builder)]
945 struct CustomParam<'w, 's> {
946 query: Query<'w, 's, ()>,
947 local: Local<'s, usize>,
948 }
949
950 #[test]
951 fn custom_param_builder() {
952 let mut world = World::new();
953
954 world.spawn(A);
955 world.spawn_empty();
956
957 let system = (CustomParamBuilder {
958 local: LocalBuilder(100),
959 query: QueryParamBuilder::new(|builder| {
960 builder.with::<A>();
961 }),
962 },)
963 .build_state(&mut world)
964 .build_system(|param: CustomParam| *param.local + param.query.iter().count());
965
966 let output = world.run_system_once(system).unwrap();
967 assert_eq!(output, 101);
968 }
969
970 #[test]
971 fn filtered_resource_conflicts_read_with_res() {
972 let mut world = World::new();
973 (
974 ParamBuilder::resource(),
975 FilteredResourcesParamBuilder::new(|builder| {
976 builder.add_read::<R>();
977 }),
978 )
979 .build_state(&mut world)
980 .build_system(|_r: Res<R>, _fr: FilteredResources| {});
981 }
982
983 #[test]
984 #[should_panic]
985 fn filtered_resource_conflicts_read_with_resmut() {
986 let mut world = World::new();
987 (
988 ParamBuilder::resource_mut(),
989 FilteredResourcesParamBuilder::new(|builder| {
990 builder.add_read::<R>();
991 }),
992 )
993 .build_state(&mut world)
994 .build_system(|_r: ResMut<R>, _fr: FilteredResources| {});
995 }
996
997 #[test]
998 #[should_panic]
999 fn filtered_resource_conflicts_read_all_with_resmut() {
1000 let mut world = World::new();
1001 (
1002 ParamBuilder::resource_mut(),
1003 FilteredResourcesParamBuilder::new(|builder| {
1004 builder.add_read_all();
1005 }),
1006 )
1007 .build_state(&mut world)
1008 .build_system(|_r: ResMut<R>, _fr: FilteredResources| {});
1009 }
1010
1011 #[test]
1012 fn filtered_resource_mut_conflicts_read_with_res() {
1013 let mut world = World::new();
1014 (
1015 ParamBuilder::resource(),
1016 FilteredResourcesMutParamBuilder::new(|builder| {
1017 builder.add_read::<R>();
1018 }),
1019 )
1020 .build_state(&mut world)
1021 .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1022 }
1023
1024 #[test]
1025 #[should_panic]
1026 fn filtered_resource_mut_conflicts_read_with_resmut() {
1027 let mut world = World::new();
1028 (
1029 ParamBuilder::resource_mut(),
1030 FilteredResourcesMutParamBuilder::new(|builder| {
1031 builder.add_read::<R>();
1032 }),
1033 )
1034 .build_state(&mut world)
1035 .build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
1036 }
1037
1038 #[test]
1039 #[should_panic]
1040 fn filtered_resource_mut_conflicts_write_with_res() {
1041 let mut world = World::new();
1042 (
1043 ParamBuilder::resource(),
1044 FilteredResourcesMutParamBuilder::new(|builder| {
1045 builder.add_write::<R>();
1046 }),
1047 )
1048 .build_state(&mut world)
1049 .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1050 }
1051
1052 #[test]
1053 #[should_panic]
1054 fn filtered_resource_mut_conflicts_write_all_with_res() {
1055 let mut world = World::new();
1056 (
1057 ParamBuilder::resource(),
1058 FilteredResourcesMutParamBuilder::new(|builder| {
1059 builder.add_write_all();
1060 }),
1061 )
1062 .build_state(&mut world)
1063 .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1064 }
1065
1066 #[test]
1067 #[should_panic]
1068 fn filtered_resource_mut_conflicts_write_with_resmut() {
1069 let mut world = World::new();
1070 (
1071 ParamBuilder::resource_mut(),
1072 FilteredResourcesMutParamBuilder::new(|builder| {
1073 builder.add_write::<R>();
1074 }),
1075 )
1076 .build_state(&mut world)
1077 .build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
1078 }
1079
1080 #[test]
1081 fn filtered_resource_reflect() {
1082 let mut world = World::new();
1083 world.insert_resource(R { foo: 7 });
1084
1085 let system = (FilteredResourcesParamBuilder::new(|builder| {
1086 builder.add_read::<R>();
1087 }),)
1088 .build_state(&mut world)
1089 .build_system(|res: FilteredResources| {
1090 let reflect_resource = <ReflectResource as FromType<R>>::from_type();
1091 let ReflectRef::Struct(reflect_struct) =
1092 reflect_resource.reflect(res).unwrap().reflect_ref()
1093 else {
1094 panic!()
1095 };
1096 *reflect_struct
1097 .field("foo")
1098 .unwrap()
1099 .try_downcast_ref::<usize>()
1100 .unwrap()
1101 });
1102
1103 let output = world.run_system_once(system).unwrap();
1104 assert_eq!(output, 7);
1105 }
1106}