1use alloc::{boxed::Box, vec::Vec};
2use bevy_platform::cell::SyncCell;
3use variadics_please::all_tuples;
4
5use crate::{
6 prelude::QueryBuilder,
7 query::{QueryData, QueryFilter, QueryState},
8 resource::Resource,
9 system::{
10 DynSystemParam, DynSystemParamState, If, Local, ParamSet, Query, SystemParam,
11 SystemParamValidationError,
12 },
13 world::{
14 FilteredResources, FilteredResourcesBuilder, FilteredResourcesMut,
15 FilteredResourcesMutBuilder, FromWorld, World,
16 },
17};
18use core::fmt::Debug;
19
20use super::{Res, ResMut, SystemState};
21
22pub unsafe trait SystemParamBuilder<P: SystemParam>: Sized {
113 fn build(self, world: &mut World) -> P::State;
116
117 fn build_state(self, world: &mut World) -> SystemState<P> {
120 SystemState::from_builder(world, self)
121 }
122}
123
124#[derive(Default, Debug, Clone)]
164pub struct ParamBuilder;
165
166unsafe impl<P: SystemParam> SystemParamBuilder<P> for ParamBuilder {
168 fn build(self, world: &mut World) -> P::State {
169 P::init_state(world)
170 }
171}
172
173impl ParamBuilder {
174 pub fn of<T: SystemParam>() -> impl SystemParamBuilder<T> {
176 Self
177 }
178
179 pub fn resource<'w, T: Resource>() -> impl SystemParamBuilder<Res<'w, T>> {
181 Self
182 }
183
184 pub fn resource_mut<'w, T: Resource>() -> impl SystemParamBuilder<ResMut<'w, T>> {
186 Self
187 }
188
189 pub fn local<'s, T: FromWorld + Send + 'static>() -> impl SystemParamBuilder<Local<'s, T>> {
191 Self
192 }
193
194 pub fn query<'w, 's, D: QueryData + 'static>() -> impl SystemParamBuilder<Query<'w, 's, D, ()>>
196 {
197 Self
198 }
199
200 pub fn query_filtered<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>(
202 ) -> impl SystemParamBuilder<Query<'w, 's, D, F>> {
203 Self
204 }
205}
206
207unsafe impl<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>
210 SystemParamBuilder<Query<'w, 's, D, F>> for QueryState<D, F>
211{
212 fn build(self, world: &mut World) -> QueryState<D, F> {
213 self.validate_world(world.id());
214 self
215 }
216}
217
218#[derive(Clone)]
259pub struct QueryParamBuilder<T>(T);
260
261impl<T> QueryParamBuilder<T> {
262 pub fn new<D: QueryData, F: QueryFilter>(f: T) -> Self
264 where
265 T: FnOnce(&mut QueryBuilder<D, F>),
266 {
267 Self(f)
268 }
269}
270
271impl<'a, D: QueryData, F: QueryFilter>
272 QueryParamBuilder<Box<dyn FnOnce(&mut QueryBuilder<D, F>) + 'a>>
273{
274 pub fn new_box(f: impl FnOnce(&mut QueryBuilder<D, F>) + 'a) -> Self {
277 Self(Box::new(f))
278 }
279}
280
281unsafe impl<
284 'w,
285 's,
286 D: QueryData + 'static,
287 F: QueryFilter + 'static,
288 T: FnOnce(&mut QueryBuilder<D, F>),
289 > SystemParamBuilder<Query<'w, 's, D, F>> for QueryParamBuilder<T>
290{
291 fn build(self, world: &mut World) -> QueryState<D, F> {
292 let mut builder = QueryBuilder::new(world);
293 (self.0)(&mut builder);
294 builder.build()
295 }
296}
297
298macro_rules! impl_system_param_builder_tuple {
299 ($(#[$meta:meta])* $(($param: ident, $builder: ident)),*) => {
300 #[expect(
301 clippy::allow_attributes,
302 reason = "This is in a macro; as such, the below lints may not always apply."
303 )]
304 #[allow(
305 unused_variables,
306 reason = "Zero-length tuples won't use any of the parameters."
307 )]
308 #[allow(
309 non_snake_case,
310 reason = "The variable names are provided by the macro caller, not by us."
311 )]
312 $(#[$meta])*
313 unsafe impl<$($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<($($param,)*)> for ($($builder,)*) {
315 fn build(self, world: &mut World) -> <($($param,)*) as SystemParam>::State {
316 let ($($builder,)*) = self;
317 #[allow(
318 clippy::unused_unit,
319 reason = "Zero-length tuples won't generate any calls to the system parameter builders."
320 )]
321 ($($builder.build(world),)*)
322 }
323 }
324 };
325}
326
327all_tuples!(
328 #[doc(fake_variadic)]
329 impl_system_param_builder_tuple,
330 0,
331 16,
332 P,
333 B
334);
335
336unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<Vec<P>> for Vec<B> {
338 fn build(self, world: &mut World) -> <Vec<P> as SystemParam>::State {
339 self.into_iter()
340 .map(|builder| builder.build(world))
341 .collect()
342 }
343}
344
345#[derive(Debug, Default, Clone)]
417pub struct ParamSetBuilder<T>(pub T);
418
419macro_rules! impl_param_set_builder_tuple {
420 ($(($param: ident, $builder: ident)),*) => {
421 #[expect(
422 clippy::allow_attributes,
423 reason = "This is in a macro; as such, the below lints may not always apply."
424 )]
425 #[allow(
426 unused_variables,
427 reason = "Zero-length tuples won't use any of the parameters."
428 )]
429 #[allow(
430 non_snake_case,
431 reason = "The variable names are provided by the macro caller, not by us."
432 )]
433 unsafe impl<'w, 's, $($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<ParamSet<'w, 's, ($($param,)*)>> for ParamSetBuilder<($($builder,)*)> {
435 fn build(self, world: &mut World) -> <($($param,)*) as SystemParam>::State {
436 let ParamSetBuilder(($($builder,)*)) = self;
437 ($($builder.build(world),)*)
438 }
439 }
440 };
441}
442
443all_tuples!(impl_param_set_builder_tuple, 1, 8, P, B);
444
445unsafe impl<'w, 's, P: SystemParam, B: SystemParamBuilder<P>>
447 SystemParamBuilder<ParamSet<'w, 's, Vec<P>>> for ParamSetBuilder<Vec<B>>
448{
449 fn build(self, world: &mut World) -> <Vec<P> as SystemParam>::State {
450 self.0
451 .into_iter()
452 .map(|builder| builder.build(world))
453 .collect()
454 }
455}
456
457pub struct DynParamBuilder<'a>(Box<dyn FnOnce(&mut World) -> DynSystemParamState + 'a>);
460
461impl<'a> DynParamBuilder<'a> {
462 pub fn new<T: SystemParam + 'static>(builder: impl SystemParamBuilder<T> + 'a) -> Self {
465 Self(Box::new(|world| {
466 DynSystemParamState::new::<T>(builder.build(world))
467 }))
468 }
469}
470
471unsafe impl<'a, 'w, 's> SystemParamBuilder<DynSystemParam<'w, 's>> for DynParamBuilder<'a> {
475 fn build(self, world: &mut World) -> <DynSystemParam<'w, 's> as SystemParam>::State {
476 (self.0)(world)
477 }
478}
479
480#[derive(Default, Debug, Clone)]
500pub struct LocalBuilder<T>(pub T);
501
502unsafe impl<'s, T: FromWorld + Send + 'static> SystemParamBuilder<Local<'s, T>>
504 for LocalBuilder<T>
505{
506 fn build(self, _world: &mut World) -> <Local<'s, T> as SystemParam>::State {
507 SyncCell::new(self.0)
508 }
509}
510
511#[derive(Clone)]
514pub struct FilteredResourcesParamBuilder<T>(T);
515
516impl<T> FilteredResourcesParamBuilder<T> {
517 pub fn new(f: T) -> Self
519 where
520 T: FnOnce(&mut FilteredResourcesBuilder),
521 {
522 Self(f)
523 }
524}
525
526impl<'a> FilteredResourcesParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesBuilder) + 'a>> {
527 pub fn new_box(f: impl FnOnce(&mut FilteredResourcesBuilder) + 'a) -> Self {
530 Self(Box::new(f))
531 }
532}
533
534unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesBuilder)>
536 SystemParamBuilder<FilteredResources<'w, 's>> for FilteredResourcesParamBuilder<T>
537{
538 fn build(self, world: &mut World) -> <FilteredResources<'w, 's> as SystemParam>::State {
539 let mut builder = FilteredResourcesBuilder::new(world);
540 (self.0)(&mut builder);
541 builder.build()
542 }
543}
544
545#[derive(Clone)]
548pub struct FilteredResourcesMutParamBuilder<T>(T);
549
550impl<T> FilteredResourcesMutParamBuilder<T> {
551 pub fn new(f: T) -> Self
553 where
554 T: FnOnce(&mut FilteredResourcesMutBuilder),
555 {
556 Self(f)
557 }
558}
559
560impl<'a> FilteredResourcesMutParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesMutBuilder) + 'a>> {
561 pub fn new_box(f: impl FnOnce(&mut FilteredResourcesMutBuilder) + 'a) -> Self {
564 Self(Box::new(f))
565 }
566}
567
568unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesMutBuilder)>
570 SystemParamBuilder<FilteredResourcesMut<'w, 's>> for FilteredResourcesMutParamBuilder<T>
571{
572 fn build(self, world: &mut World) -> <FilteredResourcesMut<'w, 's> as SystemParam>::State {
573 let mut builder = FilteredResourcesMutBuilder::new(world);
574 (self.0)(&mut builder);
575 builder.build()
576 }
577}
578
579#[derive(Clone)]
581pub struct OptionBuilder<T>(T);
582
583unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<Option<P>>
585 for OptionBuilder<B>
586{
587 fn build(self, world: &mut World) -> <Option<P> as SystemParam>::State {
588 self.0.build(world)
589 }
590}
591
592#[derive(Clone)]
594pub struct ResultBuilder<T>(T);
595
596unsafe impl<P: SystemParam, B: SystemParamBuilder<P>>
598 SystemParamBuilder<Result<P, SystemParamValidationError>> for ResultBuilder<B>
599{
600 fn build(
601 self,
602 world: &mut World,
603 ) -> <Result<P, SystemParamValidationError> as SystemParam>::State {
604 self.0.build(world)
605 }
606}
607
608#[derive(Clone)]
610pub struct IfBuilder<T>(T);
611
612unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<If<P>> for IfBuilder<B> {
614 fn build(self, world: &mut World) -> <If<P> as SystemParam>::State {
615 self.0.build(world)
616 }
617}
618
619#[cfg(test)]
620mod tests {
621 use crate::{
622 entity::Entities,
623 error::Result,
624 prelude::{Component, Query},
625 reflect::ReflectResource,
626 system::{Local, RunSystemOnce},
627 };
628 use alloc::vec;
629 use bevy_reflect::{FromType, Reflect, ReflectRef};
630
631 use super::*;
632
633 #[derive(Component)]
634 struct A;
635
636 #[derive(Component)]
637 struct B;
638
639 #[derive(Component)]
640 struct C;
641
642 #[derive(Resource, Default, Reflect)]
643 #[reflect(Resource)]
644 struct R {
645 foo: usize,
646 }
647
648 fn local_system(local: Local<u64>) -> u64 {
649 *local
650 }
651
652 fn query_system(query: Query<()>) -> usize {
653 query.iter().count()
654 }
655
656 fn query_system_result(query: Query<()>) -> Result<usize> {
657 Ok(query.iter().count())
658 }
659
660 fn multi_param_system(a: Local<u64>, b: Local<u64>) -> u64 {
661 *a + *b + 1
662 }
663
664 #[test]
665 fn local_builder() {
666 let mut world = World::new();
667
668 let system = (LocalBuilder(10),)
669 .build_state(&mut world)
670 .build_system(local_system);
671
672 let output = world.run_system_once(system).unwrap();
673 assert_eq!(output, 10);
674 }
675
676 #[test]
677 fn query_builder() {
678 let mut world = World::new();
679
680 world.spawn(A);
681 world.spawn_empty();
682
683 let system = (QueryParamBuilder::new(|query| {
684 query.with::<A>();
685 }),)
686 .build_state(&mut world)
687 .build_system(query_system);
688
689 let output = world.run_system_once(system).unwrap();
690 assert_eq!(output, 1);
691 }
692
693 #[test]
694 fn query_builder_result_fallible() {
695 let mut world = World::new();
696
697 world.spawn(A);
698 world.spawn_empty();
699
700 let system = (QueryParamBuilder::new(|query| {
701 query.with::<A>();
702 }),)
703 .build_state(&mut world)
704 .build_system(query_system_result);
705
706 let output: usize = world.run_system_once(system).unwrap();
709 assert_eq!(output, 1);
710 }
711
712 #[test]
713 fn query_builder_result_infallible() {
714 let mut world = World::new();
715
716 world.spawn(A);
717 world.spawn_empty();
718
719 let system = (QueryParamBuilder::new(|query| {
720 query.with::<A>();
721 }),)
722 .build_state(&mut world)
723 .build_system(query_system_result);
724
725 let output: Result<usize> = world.run_system_once(system).unwrap();
728 assert_eq!(output.unwrap(), 1);
729 }
730
731 #[test]
732 fn query_builder_state() {
733 let mut world = World::new();
734
735 world.spawn(A);
736 world.spawn_empty();
737
738 let state = QueryBuilder::new(&mut world).with::<A>().build();
739
740 let system = (state,).build_state(&mut world).build_system(query_system);
741
742 let output = world.run_system_once(system).unwrap();
743 assert_eq!(output, 1);
744 }
745
746 #[test]
747 fn multi_param_builder() {
748 let mut world = World::new();
749
750 world.spawn(A);
751 world.spawn_empty();
752
753 let system = (LocalBuilder(0), ParamBuilder)
754 .build_state(&mut world)
755 .build_system(multi_param_system);
756
757 let output = world.run_system_once(system).unwrap();
758 assert_eq!(output, 1);
759 }
760
761 #[test]
762 fn vec_builder() {
763 let mut world = World::new();
764
765 world.spawn((A, B, C));
766 world.spawn((A, B));
767 world.spawn((A, C));
768 world.spawn((A, C));
769 world.spawn_empty();
770
771 let system = (vec![
772 QueryParamBuilder::new_box(|builder| {
773 builder.with::<B>().without::<C>();
774 }),
775 QueryParamBuilder::new_box(|builder| {
776 builder.with::<C>().without::<B>();
777 }),
778 ],)
779 .build_state(&mut world)
780 .build_system(|params: Vec<Query<&mut A>>| {
781 let mut count: usize = 0;
782 params
783 .into_iter()
784 .for_each(|mut query| count += query.iter_mut().count());
785 count
786 });
787
788 let output = world.run_system_once(system).unwrap();
789 assert_eq!(output, 3);
790 }
791
792 #[test]
793 fn multi_param_builder_inference() {
794 let mut world = World::new();
795
796 world.spawn(A);
797 world.spawn_empty();
798
799 let system = (LocalBuilder(0u64), ParamBuilder::local::<u64>())
800 .build_state(&mut world)
801 .build_system(|a, b| *a + *b + 1);
802
803 let output = world.run_system_once(system).unwrap();
804 assert_eq!(output, 1);
805 }
806
807 #[test]
808 fn param_set_builder() {
809 let mut world = World::new();
810
811 world.spawn((A, B, C));
812 world.spawn((A, B));
813 world.spawn((A, C));
814 world.spawn((A, C));
815 world.spawn_empty();
816
817 let system = (ParamSetBuilder((
818 QueryParamBuilder::new(|builder| {
819 builder.with::<B>();
820 }),
821 QueryParamBuilder::new(|builder| {
822 builder.with::<C>();
823 }),
824 )),)
825 .build_state(&mut world)
826 .build_system(|mut params: ParamSet<(Query<&mut A>, Query<&mut A>)>| {
827 params.p0().iter().count() + params.p1().iter().count()
828 });
829
830 let output = world.run_system_once(system).unwrap();
831 assert_eq!(output, 5);
832 }
833
834 #[test]
835 fn param_set_vec_builder() {
836 let mut world = World::new();
837
838 world.spawn((A, B, C));
839 world.spawn((A, B));
840 world.spawn((A, C));
841 world.spawn((A, C));
842 world.spawn_empty();
843
844 let system = (ParamSetBuilder(vec![
845 QueryParamBuilder::new_box(|builder| {
846 builder.with::<B>();
847 }),
848 QueryParamBuilder::new_box(|builder| {
849 builder.with::<C>();
850 }),
851 ]),)
852 .build_state(&mut world)
853 .build_system(|mut params: ParamSet<Vec<Query<&mut A>>>| {
854 let mut count = 0;
855 params.for_each(|mut query| count += query.iter_mut().count());
856 count
857 });
858
859 let output = world.run_system_once(system).unwrap();
860 assert_eq!(output, 5);
861 }
862
863 #[test]
864 fn dyn_builder() {
865 let mut world = World::new();
866
867 world.spawn(A);
868 world.spawn_empty();
869
870 let system = (
871 DynParamBuilder::new(LocalBuilder(3_usize)),
872 DynParamBuilder::new::<Query<()>>(QueryParamBuilder::new(|builder| {
873 builder.with::<A>();
874 })),
875 DynParamBuilder::new::<&Entities>(ParamBuilder),
876 )
877 .build_state(&mut world)
878 .build_system(
879 |mut p0: DynSystemParam, mut p1: DynSystemParam, mut p2: DynSystemParam| {
880 let local = *p0.downcast_mut::<Local<usize>>().unwrap();
881 let query_count = p1.downcast_mut::<Query<()>>().unwrap().iter().count();
882 let _entities = p2.downcast_mut::<&Entities>().unwrap();
883 assert!(p0.downcast_mut::<Query<()>>().is_none());
884 local + query_count
885 },
886 );
887
888 let output = world.run_system_once(system).unwrap();
889 assert_eq!(output, 4);
890 }
891
892 #[derive(SystemParam)]
893 #[system_param(builder)]
894 struct CustomParam<'w, 's> {
895 query: Query<'w, 's, ()>,
896 local: Local<'s, usize>,
897 }
898
899 #[test]
900 fn custom_param_builder() {
901 let mut world = World::new();
902
903 world.spawn(A);
904 world.spawn_empty();
905
906 let system = (CustomParamBuilder {
907 local: LocalBuilder(100),
908 query: QueryParamBuilder::new(|builder| {
909 builder.with::<A>();
910 }),
911 },)
912 .build_state(&mut world)
913 .build_system(|param: CustomParam| *param.local + param.query.iter().count());
914
915 let output = world.run_system_once(system).unwrap();
916 assert_eq!(output, 101);
917 }
918
919 #[test]
920 fn filtered_resource_conflicts_read_with_res() {
921 let mut world = World::new();
922 (
923 ParamBuilder::resource(),
924 FilteredResourcesParamBuilder::new(|builder| {
925 builder.add_read::<R>();
926 }),
927 )
928 .build_state(&mut world)
929 .build_system(|_r: Res<R>, _fr: FilteredResources| {});
930 }
931
932 #[test]
933 #[should_panic]
934 fn filtered_resource_conflicts_read_with_resmut() {
935 let mut world = World::new();
936 (
937 ParamBuilder::resource_mut(),
938 FilteredResourcesParamBuilder::new(|builder| {
939 builder.add_read::<R>();
940 }),
941 )
942 .build_state(&mut world)
943 .build_system(|_r: ResMut<R>, _fr: FilteredResources| {});
944 }
945
946 #[test]
947 #[should_panic]
948 fn filtered_resource_conflicts_read_all_with_resmut() {
949 let mut world = World::new();
950 (
951 ParamBuilder::resource_mut(),
952 FilteredResourcesParamBuilder::new(|builder| {
953 builder.add_read_all();
954 }),
955 )
956 .build_state(&mut world)
957 .build_system(|_r: ResMut<R>, _fr: FilteredResources| {});
958 }
959
960 #[test]
961 fn filtered_resource_mut_conflicts_read_with_res() {
962 let mut world = World::new();
963 (
964 ParamBuilder::resource(),
965 FilteredResourcesMutParamBuilder::new(|builder| {
966 builder.add_read::<R>();
967 }),
968 )
969 .build_state(&mut world)
970 .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
971 }
972
973 #[test]
974 #[should_panic]
975 fn filtered_resource_mut_conflicts_read_with_resmut() {
976 let mut world = World::new();
977 (
978 ParamBuilder::resource_mut(),
979 FilteredResourcesMutParamBuilder::new(|builder| {
980 builder.add_read::<R>();
981 }),
982 )
983 .build_state(&mut world)
984 .build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
985 }
986
987 #[test]
988 #[should_panic]
989 fn filtered_resource_mut_conflicts_write_with_res() {
990 let mut world = World::new();
991 (
992 ParamBuilder::resource(),
993 FilteredResourcesMutParamBuilder::new(|builder| {
994 builder.add_write::<R>();
995 }),
996 )
997 .build_state(&mut world)
998 .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
999 }
1000
1001 #[test]
1002 #[should_panic]
1003 fn filtered_resource_mut_conflicts_write_all_with_res() {
1004 let mut world = World::new();
1005 (
1006 ParamBuilder::resource(),
1007 FilteredResourcesMutParamBuilder::new(|builder| {
1008 builder.add_write_all();
1009 }),
1010 )
1011 .build_state(&mut world)
1012 .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1013 }
1014
1015 #[test]
1016 #[should_panic]
1017 fn filtered_resource_mut_conflicts_write_with_resmut() {
1018 let mut world = World::new();
1019 (
1020 ParamBuilder::resource_mut(),
1021 FilteredResourcesMutParamBuilder::new(|builder| {
1022 builder.add_write::<R>();
1023 }),
1024 )
1025 .build_state(&mut world)
1026 .build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
1027 }
1028
1029 #[test]
1030 fn filtered_resource_reflect() {
1031 let mut world = World::new();
1032 world.insert_resource(R { foo: 7 });
1033
1034 let system = (FilteredResourcesParamBuilder::new(|builder| {
1035 builder.add_read::<R>();
1036 }),)
1037 .build_state(&mut world)
1038 .build_system(|res: FilteredResources| {
1039 let reflect_resource = <ReflectResource as FromType<R>>::from_type();
1040 let ReflectRef::Struct(reflect_struct) =
1041 reflect_resource.reflect(res).unwrap().reflect_ref()
1042 else {
1043 panic!()
1044 };
1045 *reflect_struct
1046 .field("foo")
1047 .unwrap()
1048 .try_downcast_ref::<usize>()
1049 .unwrap()
1050 });
1051
1052 let output = world.run_system_once(system).unwrap();
1053 assert_eq!(output, 7);
1054 }
1055}