1#[cfg(feature = "hotpatching")]
2use crate::{change_detection::DetectChanges, HotPatchChanges};
3use crate::{
4 change_detection::Mut,
5 entity::Entity,
6 entity_disabling::Internal,
7 error::BevyError,
8 system::{
9 input::SystemInput, BoxedSystem, IntoSystem, RunSystemError, SystemParamValidationError,
10 },
11 world::World,
12};
13use alloc::boxed::Box;
14use bevy_ecs_macros::{Component, Resource};
15use bevy_utils::prelude::DebugName;
16use core::{any::TypeId, marker::PhantomData};
17use thiserror::Error;
18
19#[derive(Component)]
21#[require(SystemIdMarker = SystemIdMarker::typed_system_id_marker::<I, O>(), Internal)]
22pub(crate) struct RegisteredSystem<I, O> {
23 initialized: bool,
24 system: BoxedSystem<I, O>,
25}
26
27impl<I, O> RegisteredSystem<I, O> {
28 pub fn new(system: BoxedSystem<I, O>) -> Self {
29 RegisteredSystem {
30 initialized: false,
31 system,
32 }
33 }
34}
35
36#[derive(Debug, Clone)]
37struct TypeIdAndName {
38 type_id: TypeId,
39 name: DebugName,
40}
41
42impl TypeIdAndName {
43 fn new<T: 'static>() -> Self {
44 Self {
45 type_id: TypeId::of::<T>(),
46 name: DebugName::type_name::<T>(),
47 }
48 }
49}
50
51impl Default for TypeIdAndName {
52 fn default() -> Self {
53 Self {
54 type_id: TypeId::of::<()>(),
55 name: DebugName::type_name::<()>(),
56 }
57 }
58}
59
60#[derive(Debug, Default, Clone, Component)]
62pub struct SystemIdMarker {
63 input_type_id: TypeIdAndName,
64 output_type_id: TypeIdAndName,
65}
66
67impl SystemIdMarker {
68 fn typed_system_id_marker<I: 'static, O: 'static>() -> Self {
69 Self {
70 input_type_id: TypeIdAndName::new::<I>(),
71 output_type_id: TypeIdAndName::new::<O>(),
72 }
73 }
74}
75
76pub struct RemovedSystem<I = (), O = ()> {
81 initialized: bool,
82 system: BoxedSystem<I, O>,
83}
84
85impl<I, O> RemovedSystem<I, O> {
86 pub fn initialized(&self) -> bool {
89 self.initialized
90 }
91
92 pub fn system(self) -> BoxedSystem<I, O> {
94 self.system
95 }
96}
97
98pub struct SystemId<I: SystemInput = (), O = ()> {
103 pub(crate) entity: Entity,
104 pub(crate) marker: PhantomData<fn(I) -> O>,
105}
106
107impl<I: SystemInput, O> SystemId<I, O> {
108 pub fn entity(self) -> Entity {
115 self.entity
116 }
117
118 pub fn from_entity(entity: Entity) -> Self {
123 Self {
124 entity,
125 marker: PhantomData,
126 }
127 }
128}
129
130impl<I: SystemInput, O> Eq for SystemId<I, O> {}
131
132impl<I: SystemInput, O> Copy for SystemId<I, O> {}
134
135impl<I: SystemInput, O> Clone for SystemId<I, O> {
137 fn clone(&self) -> Self {
138 *self
139 }
140}
141
142impl<I: SystemInput, O> PartialEq for SystemId<I, O> {
144 fn eq(&self, other: &Self) -> bool {
145 self.entity == other.entity && self.marker == other.marker
146 }
147}
148
149impl<I: SystemInput, O> core::hash::Hash for SystemId<I, O> {
151 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
152 self.entity.hash(state);
153 }
154}
155
156impl<I: SystemInput, O> core::fmt::Debug for SystemId<I, O> {
157 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
158 f.debug_tuple("SystemId").field(&self.entity).finish()
159 }
160}
161
162#[derive(Resource)]
166pub struct CachedSystemId<S> {
167 pub entity: Entity,
169 _marker: PhantomData<fn() -> S>,
170}
171
172impl<S> CachedSystemId<S> {
173 pub fn new<I: SystemInput, O>(id: SystemId<I, O>) -> Self {
175 Self {
176 entity: id.entity(),
177 _marker: PhantomData,
178 }
179 }
180}
181
182impl World {
183 pub fn register_system<I, O, M>(
195 &mut self,
196 system: impl IntoSystem<I, O, M> + 'static,
197 ) -> SystemId<I, O>
198 where
199 I: SystemInput + 'static,
200 O: 'static,
201 {
202 self.register_boxed_system(Box::new(IntoSystem::into_system(system)))
203 }
204
205 pub fn register_boxed_system<I, O>(&mut self, system: BoxedSystem<I, O>) -> SystemId<I, O>
210 where
211 I: SystemInput + 'static,
212 O: 'static,
213 {
214 let entity = self.spawn(RegisteredSystem::new(system)).id();
215 SystemId::from_entity(entity)
216 }
217
218 pub fn unregister_system<I, O>(
225 &mut self,
226 id: SystemId<I, O>,
227 ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
228 where
229 I: SystemInput + 'static,
230 O: 'static,
231 {
232 match self.get_entity_mut(id.entity) {
233 Ok(mut entity) => {
234 let registered_system = entity
235 .take::<RegisteredSystem<I, O>>()
236 .ok_or(RegisteredSystemError::SelfRemove(id))?;
237 entity.despawn();
238 Ok(RemovedSystem {
239 initialized: registered_system.initialized,
240 system: registered_system.system,
241 })
242 }
243 Err(_) => Err(RegisteredSystemError::SystemIdNotRegistered(id)),
244 }
245 }
246
247 pub fn run_system<O: 'static>(
333 &mut self,
334 id: SystemId<(), O>,
335 ) -> Result<O, RegisteredSystemError<(), O>> {
336 self.run_system_with(id, ())
337 }
338
339 pub fn run_system_with<I, O>(
364 &mut self,
365 id: SystemId<I, O>,
366 input: I::Inner<'_>,
367 ) -> Result<O, RegisteredSystemError<I, O>>
368 where
369 I: SystemInput + 'static,
370 O: 'static,
371 {
372 let mut entity = self
374 .get_entity_mut(id.entity)
375 .map_err(|_| RegisteredSystemError::SystemIdNotRegistered(id))?;
376
377 let Some(RegisteredSystem {
379 mut initialized,
380 mut system,
381 }) = entity.take::<RegisteredSystem<I, O>>()
382 else {
383 let Some(system_id_marker) = entity.get::<SystemIdMarker>() else {
384 return Err(RegisteredSystemError::SystemIdNotRegistered(id));
385 };
386 if system_id_marker.input_type_id.type_id != TypeId::of::<I>()
387 || system_id_marker.output_type_id.type_id != TypeId::of::<O>()
388 {
389 return Err(RegisteredSystemError::IncorrectType(
390 id,
391 system_id_marker.clone(),
392 ));
393 }
394 return Err(RegisteredSystemError::Recursive(id));
395 };
396
397 if !initialized {
399 system.initialize(self);
400 initialized = true;
401 }
402
403 #[cfg(feature = "hotpatching")]
405 if self
406 .get_resource_ref::<HotPatchChanges>()
407 .map(|r| r.last_changed())
408 .unwrap_or_default()
409 .is_newer_than(system.get_last_run(), self.change_tick())
410 {
411 system.refresh_hotpatch();
412 }
413
414 let result = system.run_without_applying_deferred(input, self);
417 system.queue_deferred(self.into());
418
419 if let Ok(mut entity) = self.get_entity_mut(id.entity) {
421 entity.insert::<RegisteredSystem<I, O>>(RegisteredSystem {
422 initialized,
423 system,
424 });
425 }
426
427 self.flush();
429 Ok(result?)
430 }
431
432 pub fn register_system_cached<I, O, M, S>(&mut self, system: S) -> SystemId<I, O>
452 where
453 I: SystemInput + 'static,
454 O: 'static,
455 S: IntoSystem<I, O, M> + 'static,
456 {
457 const {
458 assert!(
459 size_of::<S>() == 0,
460 "Non-ZST systems (e.g. capturing closures, function pointers) cannot be cached.",
461 );
462 }
463
464 if !self.contains_resource::<CachedSystemId<S>>() {
465 let id = self.register_system(system);
466 self.insert_resource(CachedSystemId::<S>::new(id));
467 return id;
468 }
469
470 self.resource_scope(|world, mut id: Mut<CachedSystemId<S>>| {
471 if let Ok(mut entity) = world.get_entity_mut(id.entity) {
472 if !entity.contains::<RegisteredSystem<I, O>>() {
473 entity.insert(RegisteredSystem::new(Box::new(IntoSystem::into_system(
474 system,
475 ))));
476 }
477 } else {
478 id.entity = world.register_system(system).entity();
479 }
480 SystemId::from_entity(id.entity)
481 })
482 }
483
484 pub fn unregister_system_cached<I, O, M, S>(
488 &mut self,
489 _system: S,
490 ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
491 where
492 I: SystemInput + 'static,
493 O: 'static,
494 S: IntoSystem<I, O, M> + 'static,
495 {
496 let id = self
497 .remove_resource::<CachedSystemId<S>>()
498 .ok_or(RegisteredSystemError::SystemNotCached)?;
499 self.unregister_system(SystemId::<I, O>::from_entity(id.entity))
500 }
501
502 pub fn run_system_cached<O: 'static, M, S: IntoSystem<(), O, M> + 'static>(
506 &mut self,
507 system: S,
508 ) -> Result<O, RegisteredSystemError<(), O>> {
509 self.run_system_cached_with(system, ())
510 }
511
512 pub fn run_system_cached_with<I, O, M, S>(
516 &mut self,
517 system: S,
518 input: I::Inner<'_>,
519 ) -> Result<O, RegisteredSystemError<I, O>>
520 where
521 I: SystemInput + 'static,
522 O: 'static,
523 S: IntoSystem<I, O, M> + 'static,
524 {
525 let id = self.register_system_cached(system);
526 self.run_system_with(id, input)
527 }
528}
529
530#[derive(Error)]
532pub enum RegisteredSystemError<I: SystemInput = (), O = ()> {
533 #[error("System {0:?} was not registered")]
537 SystemIdNotRegistered(SystemId<I, O>),
538 #[error("Cached system was not found")]
542 SystemNotCached,
543 #[error("System {0:?} tried to run itself recursively")]
545 Recursive(SystemId<I, O>),
546 #[error("System {0:?} tried to remove itself")]
548 SelfRemove(SystemId<I, O>),
549 #[error("System did not run due to failed parameter validation: {0}")]
552 Skipped(SystemParamValidationError),
553 #[error("System returned error: {0}")]
555 Failed(BevyError),
556 #[error("Could not get system from `{}`, entity was `SystemId<{}, {}>`", DebugName::type_name::<SystemId<I, O>>(), .1.input_type_id.name, .1.output_type_id.name)]
558 IncorrectType(SystemId<I, O>, SystemIdMarker),
559}
560
561impl<I: SystemInput, O> From<RunSystemError> for RegisteredSystemError<I, O> {
562 fn from(value: RunSystemError) -> Self {
563 match value {
564 RunSystemError::Skipped(err) => Self::Skipped(err),
565 RunSystemError::Failed(err) => Self::Failed(err),
566 }
567 }
568}
569
570impl<I: SystemInput, O> core::fmt::Debug for RegisteredSystemError<I, O> {
571 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
572 match self {
573 Self::SystemIdNotRegistered(arg0) => {
574 f.debug_tuple("SystemIdNotRegistered").field(arg0).finish()
575 }
576 Self::SystemNotCached => write!(f, "SystemNotCached"),
577 Self::Recursive(arg0) => f.debug_tuple("Recursive").field(arg0).finish(),
578 Self::SelfRemove(arg0) => f.debug_tuple("SelfRemove").field(arg0).finish(),
579 Self::Skipped(arg0) => f.debug_tuple("Skipped").field(arg0).finish(),
580 Self::Failed(arg0) => f.debug_tuple("Failed").field(arg0).finish(),
581 Self::IncorrectType(arg0, arg1) => f
582 .debug_tuple("IncorrectType")
583 .field(arg0)
584 .field(arg1)
585 .finish(),
586 }
587 }
588}
589
590#[cfg(test)]
591mod tests {
592 use core::cell::Cell;
593
594 use bevy_utils::default;
595
596 use crate::{
597 prelude::*,
598 system::{RegisteredSystemError, SystemId},
599 };
600
601 #[derive(Resource, Default, PartialEq, Debug)]
602 struct Counter(u8);
603
604 #[test]
605 fn change_detection() {
606 #[derive(Resource, Default)]
607 struct ChangeDetector;
608
609 fn count_up_iff_changed(
610 mut counter: ResMut<Counter>,
611 change_detector: ResMut<ChangeDetector>,
612 ) {
613 if change_detector.is_changed() {
614 counter.0 += 1;
615 }
616 }
617
618 let mut world = World::new();
619 world.init_resource::<ChangeDetector>();
620 world.init_resource::<Counter>();
621 assert_eq!(*world.resource::<Counter>(), Counter(0));
622 let id = world.register_system(count_up_iff_changed);
624 world.run_system(id).expect("system runs successfully");
625 assert_eq!(*world.resource::<Counter>(), Counter(1));
626 world.run_system(id).expect("system runs successfully");
628 assert_eq!(*world.resource::<Counter>(), Counter(1));
629 world.resource_mut::<ChangeDetector>().set_changed();
631 world.run_system(id).expect("system runs successfully");
632 assert_eq!(*world.resource::<Counter>(), Counter(2));
633 }
634
635 #[test]
636 fn local_variables() {
637 fn doubling(last_counter: Local<Counter>, mut counter: ResMut<Counter>) {
639 counter.0 += last_counter.0 .0;
640 last_counter.0 .0 = counter.0;
641 }
642
643 let mut world = World::new();
644 world.insert_resource(Counter(1));
645 assert_eq!(*world.resource::<Counter>(), Counter(1));
646 let id = world.register_system(doubling);
647 world.run_system(id).expect("system runs successfully");
648 assert_eq!(*world.resource::<Counter>(), Counter(1));
649 world.run_system(id).expect("system runs successfully");
650 assert_eq!(*world.resource::<Counter>(), Counter(2));
651 world.run_system(id).expect("system runs successfully");
652 assert_eq!(*world.resource::<Counter>(), Counter(4));
653 world.run_system(id).expect("system runs successfully");
654 assert_eq!(*world.resource::<Counter>(), Counter(8));
655 }
656
657 #[test]
658 fn input_values() {
659 struct NonCopy(u8);
661
662 fn increment_sys(In(NonCopy(increment_by)): In<NonCopy>, mut counter: ResMut<Counter>) {
663 counter.0 += increment_by;
664 }
665
666 let mut world = World::new();
667
668 let id = world.register_system(increment_sys);
669
670 world.insert_resource(Counter(1));
672 assert_eq!(*world.resource::<Counter>(), Counter(1));
673
674 world
675 .run_system_with(id, NonCopy(1))
676 .expect("system runs successfully");
677 assert_eq!(*world.resource::<Counter>(), Counter(2));
678
679 world
680 .run_system_with(id, NonCopy(1))
681 .expect("system runs successfully");
682 assert_eq!(*world.resource::<Counter>(), Counter(3));
683
684 world
685 .run_system_with(id, NonCopy(20))
686 .expect("system runs successfully");
687 assert_eq!(*world.resource::<Counter>(), Counter(23));
688
689 world
690 .run_system_with(id, NonCopy(1))
691 .expect("system runs successfully");
692 assert_eq!(*world.resource::<Counter>(), Counter(24));
693 }
694
695 #[test]
696 fn output_values() {
697 #[derive(Eq, PartialEq, Debug)]
699 struct NonCopy(u8);
700
701 fn increment_sys(mut counter: ResMut<Counter>) -> NonCopy {
702 counter.0 += 1;
703 NonCopy(counter.0)
704 }
705
706 let mut world = World::new();
707
708 let id = world.register_system(increment_sys);
709
710 world.insert_resource(Counter(1));
712 assert_eq!(*world.resource::<Counter>(), Counter(1));
713
714 let output = world.run_system(id).expect("system runs successfully");
715 assert_eq!(*world.resource::<Counter>(), Counter(2));
716 assert_eq!(output, NonCopy(2));
717
718 let output = world.run_system(id).expect("system runs successfully");
719 assert_eq!(*world.resource::<Counter>(), Counter(3));
720 assert_eq!(output, NonCopy(3));
721 }
722
723 #[test]
724 fn fallible_system() {
725 fn sys() -> Result<()> {
726 Err("error")?;
727 Ok(())
728 }
729
730 let mut world = World::new();
731 let fallible_system_id = world.register_system(sys);
732 let output = world.run_system(fallible_system_id);
733 assert!(matches!(output, Ok(Err(_))));
734 }
735
736 #[test]
737 fn exclusive_system() {
738 let mut world = World::new();
739 let exclusive_system_id = world.register_system(|world: &mut World| {
740 world.spawn_empty();
741 });
742 let entity_count = world.entities.len();
743 let _ = world.run_system(exclusive_system_id);
744 assert_eq!(world.entities.len(), entity_count + 1);
745 }
746
747 #[test]
748 fn nested_systems() {
749 use crate::system::SystemId;
750
751 #[derive(Component)]
752 struct Callback(SystemId);
753
754 fn nested(query: Query<&Callback>, mut commands: Commands) {
755 for callback in query.iter() {
756 commands.run_system(callback.0);
757 }
758 }
759
760 let mut world = World::new();
761 world.insert_resource(Counter(0));
762
763 let increment_two = world.register_system(|mut counter: ResMut<Counter>| {
764 counter.0 += 2;
765 });
766 let increment_three = world.register_system(|mut counter: ResMut<Counter>| {
767 counter.0 += 3;
768 });
769 let nested_id = world.register_system(nested);
770
771 world.spawn(Callback(increment_two));
772 world.spawn(Callback(increment_three));
773 let _ = world.run_system(nested_id);
774 assert_eq!(*world.resource::<Counter>(), Counter(5));
775 }
776
777 #[test]
778 fn nested_systems_with_inputs() {
779 use crate::system::SystemId;
780
781 #[derive(Component)]
782 struct Callback(SystemId<In<u8>>, u8);
783
784 fn nested(query: Query<&Callback>, mut commands: Commands) {
785 for callback in query.iter() {
786 commands.run_system_with(callback.0, callback.1);
787 }
788 }
789
790 let mut world = World::new();
791 world.insert_resource(Counter(0));
792
793 let increment_by =
794 world.register_system(|In(amt): In<u8>, mut counter: ResMut<Counter>| {
795 counter.0 += amt;
796 });
797 let nested_id = world.register_system(nested);
798
799 world.spawn(Callback(increment_by, 2));
800 world.spawn(Callback(increment_by, 3));
801 let _ = world.run_system(nested_id);
802 assert_eq!(*world.resource::<Counter>(), Counter(5));
803 }
804
805 #[test]
806 fn cached_system() {
807 use crate::system::RegisteredSystemError;
808
809 fn four() -> i32 {
810 4
811 }
812
813 let mut world = World::new();
814 let old = world.register_system_cached(four);
815 let new = world.register_system_cached(four);
816 assert_eq!(old, new);
817
818 let result = world.unregister_system_cached(four);
819 assert!(result.is_ok());
820 let new = world.register_system_cached(four);
821 assert_ne!(old, new);
822
823 let output = world.run_system(old);
824 assert!(matches!(
825 output,
826 Err(RegisteredSystemError::SystemIdNotRegistered(x)) if x == old,
827 ));
828 let output = world.run_system(new);
829 assert!(matches!(output, Ok(x) if x == four()));
830 let output = world.run_system_cached(four);
831 assert!(matches!(output, Ok(x) if x == four()));
832 let output = world.run_system_cached_with(four, ());
833 assert!(matches!(output, Ok(x) if x == four()));
834 }
835
836 #[test]
837 fn cached_fallible_system() {
838 fn sys() -> Result<()> {
839 Err("error")?;
840 Ok(())
841 }
842
843 let mut world = World::new();
844 let fallible_system_id = world.register_system_cached(sys);
845 let output = world.run_system(fallible_system_id);
846 assert!(matches!(output, Ok(Err(_))));
847 let output = world.run_system_cached(sys);
848 assert!(matches!(output, Ok(Err(_))));
849 let output = world.run_system_cached_with(sys, ());
850 assert!(matches!(output, Ok(Err(_))));
851 }
852
853 #[test]
854 fn cached_system_commands() {
855 fn sys(mut counter: ResMut<Counter>) {
856 counter.0 += 1;
857 }
858
859 let mut world = World::new();
860 world.insert_resource(Counter(0));
861 world.commands().run_system_cached(sys);
862 world.flush_commands();
863 assert_eq!(world.resource::<Counter>().0, 1);
864 world.commands().run_system_cached_with(sys, ());
865 world.flush_commands();
866 assert_eq!(world.resource::<Counter>().0, 2);
867 }
868
869 #[test]
870 fn cached_fallible_system_commands() {
871 fn sys(mut counter: ResMut<Counter>) -> Result {
872 counter.0 += 1;
873 Ok(())
874 }
875
876 let mut world = World::new();
877 world.insert_resource(Counter(0));
878 world.commands().run_system_cached(sys);
879 world.flush_commands();
880 assert_eq!(world.resource::<Counter>().0, 1);
881 world.commands().run_system_cached_with(sys, ());
882 world.flush_commands();
883 assert_eq!(world.resource::<Counter>().0, 2);
884 }
885
886 #[test]
887 #[should_panic(expected = "This system always fails")]
888 fn cached_fallible_system_commands_can_fail() {
889 use crate::system::command;
890 fn sys() -> Result {
891 Err("This system always fails".into())
892 }
893
894 let mut world = World::new();
895 world.commands().queue(command::run_system_cached(sys));
896 world.flush_commands();
897 }
898
899 #[test]
900 fn cached_system_adapters() {
901 fn four() -> i32 {
902 4
903 }
904
905 fn double(In(i): In<i32>) -> i32 {
906 i * 2
907 }
908
909 let mut world = World::new();
910
911 let output = world.run_system_cached(four.pipe(double));
912 assert!(matches!(output, Ok(8)));
913
914 let output = world.run_system_cached(four.map(|i| i * 2));
915 assert!(matches!(output, Ok(8)));
916 }
917
918 #[test]
919 fn cached_system_into_same_system_type() {
920 struct Foo;
921 impl IntoSystem<(), (), ()> for Foo {
922 type System = ApplyDeferred;
923 fn into_system(_: Self) -> Self::System {
924 ApplyDeferred
925 }
926 }
927
928 struct Bar;
929 impl IntoSystem<(), (), ()> for Bar {
930 type System = ApplyDeferred;
931 fn into_system(_: Self) -> Self::System {
932 ApplyDeferred
933 }
934 }
935
936 let mut world = World::new();
937 let foo1 = world.register_system_cached(Foo);
938 let foo2 = world.register_system_cached(Foo);
939 let bar1 = world.register_system_cached(Bar);
940 let bar2 = world.register_system_cached(Bar);
941
942 assert_ne!(foo1, bar1);
946
947 assert_eq!(foo1, foo2);
950 assert_eq!(bar1, bar2);
951 }
952
953 #[test]
954 fn system_with_input_ref() {
955 fn with_ref(InRef(input): InRef<u8>, mut counter: ResMut<Counter>) {
956 counter.0 += *input;
957 }
958
959 let mut world = World::new();
960 world.insert_resource(Counter(0));
961
962 let id = world.register_system(with_ref);
963 world.run_system_with(id, &2).unwrap();
964 assert_eq!(*world.resource::<Counter>(), Counter(2));
965 }
966
967 #[test]
968 fn system_with_input_mut() {
969 #[derive(Event)]
970 struct MyEvent {
971 cancelled: bool,
972 }
973
974 fn post(InMut(event): InMut<MyEvent>, counter: ResMut<Counter>) {
975 if counter.0 > 0 {
976 event.cancelled = true;
977 }
978 }
979
980 let mut world = World::new();
981 world.insert_resource(Counter(0));
982 let post_system = world.register_system(post);
983
984 let mut event = MyEvent { cancelled: false };
985 world.run_system_with(post_system, &mut event).unwrap();
986 assert!(!event.cancelled);
987
988 world.resource_mut::<Counter>().0 = 1;
989 world.run_system_with(post_system, &mut event).unwrap();
990 assert!(event.cancelled);
991 }
992
993 #[test]
994 fn run_system_invalid_params() {
995 use crate::system::RegisteredSystemError;
996 use alloc::string::ToString;
997
998 struct T;
999 impl Resource for T {}
1000 fn system(_: Res<T>) {}
1001
1002 let mut world = World::new();
1003 let id = world.register_system(system);
1004 let result = world.run_system(id);
1006
1007 assert!(matches!(result, Err(RegisteredSystemError::Failed { .. })));
1008 let expected = "Resource does not exist";
1009 let actual = result.unwrap_err().to_string();
1010
1011 assert!(
1012 actual.contains(expected),
1013 "Expected error message to contain `{}` but got `{}`",
1014 expected,
1015 actual
1016 );
1017 }
1018
1019 #[test]
1020 fn run_system_recursive() {
1021 std::thread_local! {
1022 static INVOCATIONS_LEFT: Cell<i32> = const { Cell::new(3) };
1023 static SYSTEM_ID: Cell<Option<SystemId>> = default();
1024 }
1025
1026 fn system(mut commands: Commands) {
1027 let count = INVOCATIONS_LEFT.get() - 1;
1028 INVOCATIONS_LEFT.set(count);
1029 if count > 0 {
1030 commands.run_system(SYSTEM_ID.get().unwrap());
1031 }
1032 }
1033
1034 let mut world = World::new();
1035 let id = world.register_system(system);
1036 SYSTEM_ID.set(Some(id));
1037 world.run_system(id).unwrap();
1038
1039 assert_eq!(INVOCATIONS_LEFT.get(), 0);
1040 }
1041
1042 #[test]
1043 fn run_system_exclusive_adapters() {
1044 let mut world = World::new();
1045 fn system(_: &mut World) {}
1046 world.run_system_cached(system).unwrap();
1047 world.run_system_cached(system.pipe(system)).unwrap();
1048 world.run_system_cached(system.map(|()| {})).unwrap();
1049 }
1050
1051 #[test]
1052 fn wrong_system_type() {
1053 fn test() -> Result<(), u8> {
1054 Ok(())
1055 }
1056
1057 let mut world = World::new();
1058
1059 let entity = world.register_system_cached(test).entity();
1060
1061 match world.run_system::<u8>(SystemId::from_entity(entity)) {
1062 Ok(_) => panic!("Should fail since called `run_system` with wrong SystemId type."),
1063 Err(RegisteredSystemError::IncorrectType(_, _)) => (),
1064 Err(err) => panic!("Failed with wrong error. `{:?}`", err),
1065 }
1066 }
1067}