bevy_ecs/system/
system_registry.rs

1#[cfg(feature = "bevy_reflect")]
2use crate::reflect::ReflectComponent;
3use crate::{
4    change_detection::Mut,
5    entity::Entity,
6    system::{input::SystemInput, BoxedSystem, IntoSystem, SystemParamValidationError},
7    world::World,
8};
9use alloc::boxed::Box;
10use bevy_ecs_macros::{Component, Resource};
11#[cfg(feature = "bevy_reflect")]
12use bevy_reflect::{std_traits::ReflectDefault, Reflect};
13use core::marker::PhantomData;
14use thiserror::Error;
15
16/// A small wrapper for [`BoxedSystem`] that also keeps track whether or not the system has been initialized.
17#[derive(Component)]
18#[require(SystemIdMarker)]
19pub(crate) struct RegisteredSystem<I, O> {
20    initialized: bool,
21    system: BoxedSystem<I, O>,
22}
23
24impl<I, O> RegisteredSystem<I, O> {
25    pub fn new(system: BoxedSystem<I, O>) -> Self {
26        RegisteredSystem {
27            initialized: false,
28            system,
29        }
30    }
31}
32
33/// Marker [`Component`](bevy_ecs::component::Component) for identifying [`SystemId`] [`Entity`]s.
34#[derive(Component, Default)]
35#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
36#[cfg_attr(feature = "bevy_reflect", reflect(Component, Default))]
37pub struct SystemIdMarker;
38
39/// A system that has been removed from the registry.
40/// It contains the system and whether or not it has been initialized.
41///
42/// This struct is returned by [`World::unregister_system`].
43pub struct RemovedSystem<I = (), O = ()> {
44    initialized: bool,
45    system: BoxedSystem<I, O>,
46}
47
48impl<I, O> RemovedSystem<I, O> {
49    /// Is the system initialized?
50    /// A system is initialized the first time it's ran.
51    pub fn initialized(&self) -> bool {
52        self.initialized
53    }
54
55    /// The system removed from the storage.
56    pub fn system(self) -> BoxedSystem<I, O> {
57        self.system
58    }
59}
60
61/// An identifier for a registered system.
62///
63/// These are opaque identifiers, keyed to a specific [`World`],
64/// and are created via [`World::register_system`].
65pub struct SystemId<I: SystemInput = (), O = ()> {
66    pub(crate) entity: Entity,
67    pub(crate) marker: PhantomData<fn(I) -> O>,
68}
69
70impl<I: SystemInput, O> SystemId<I, O> {
71    /// Transforms a [`SystemId`] into the [`Entity`] that holds the one-shot system's state.
72    ///
73    /// It's trivial to convert [`SystemId`] into an [`Entity`] since a one-shot system
74    /// is really an entity with associated handler function.
75    ///
76    /// For example, this is useful if you want to assign a name label to a system.
77    pub fn entity(self) -> Entity {
78        self.entity
79    }
80
81    /// Create [`SystemId`] from an [`Entity`]. Useful when you only have entity handles to avoid
82    /// adding extra components that have a [`SystemId`] everywhere. To run a system with this ID
83    ///  - The entity must be a system
84    ///  - The `I` + `O` types must be correct
85    pub fn from_entity(entity: Entity) -> Self {
86        Self {
87            entity,
88            marker: PhantomData,
89        }
90    }
91}
92
93impl<I: SystemInput, O> Eq for SystemId<I, O> {}
94
95// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
96impl<I: SystemInput, O> Copy for SystemId<I, O> {}
97
98// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
99impl<I: SystemInput, O> Clone for SystemId<I, O> {
100    fn clone(&self) -> Self {
101        *self
102    }
103}
104
105// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
106impl<I: SystemInput, O> PartialEq for SystemId<I, O> {
107    fn eq(&self, other: &Self) -> bool {
108        self.entity == other.entity && self.marker == other.marker
109    }
110}
111
112// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
113impl<I: SystemInput, O> core::hash::Hash for SystemId<I, O> {
114    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
115        self.entity.hash(state);
116    }
117}
118
119impl<I: SystemInput, O> core::fmt::Debug for SystemId<I, O> {
120    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
121        f.debug_tuple("SystemId").field(&self.entity).finish()
122    }
123}
124
125/// A cached [`SystemId`] distinguished by the unique function type of its system.
126///
127/// This resource is inserted by [`World::register_system_cached`].
128#[derive(Resource)]
129pub struct CachedSystemId<S> {
130    /// The cached `SystemId` as an `Entity`.
131    pub entity: Entity,
132    _marker: PhantomData<fn() -> S>,
133}
134
135impl<S> CachedSystemId<S> {
136    /// Creates a new `CachedSystemId` struct given a `SystemId`.
137    pub fn new<I: SystemInput, O>(id: SystemId<I, O>) -> Self {
138        Self {
139            entity: id.entity(),
140            _marker: PhantomData,
141        }
142    }
143}
144
145impl World {
146    /// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`].
147    ///
148    /// It's possible to register multiple copies of the same system by calling this function
149    /// multiple times. If that's not what you want, consider using [`World::register_system_cached`]
150    /// instead.
151    ///
152    /// This is different from adding systems to a [`Schedule`](crate::schedule::Schedule),
153    /// because the [`SystemId`] that is returned can be used anywhere in the [`World`] to run the associated system.
154    /// This allows for running systems in a pushed-based fashion.
155    /// Using a [`Schedule`](crate::schedule::Schedule) is still preferred for most cases
156    /// due to its better performance and ability to run non-conflicting systems simultaneously.
157    pub fn register_system<I, O, M>(
158        &mut self,
159        system: impl IntoSystem<I, O, M> + 'static,
160    ) -> SystemId<I, O>
161    where
162        I: SystemInput + 'static,
163        O: 'static,
164    {
165        self.register_boxed_system(Box::new(IntoSystem::into_system(system)))
166    }
167
168    /// Similar to [`Self::register_system`], but allows passing in a [`BoxedSystem`].
169    ///
170    ///  This is useful if the [`IntoSystem`] implementor has already been turned into a
171    /// [`System`](crate::system::System) trait object and put in a [`Box`].
172    pub fn register_boxed_system<I, O>(&mut self, system: BoxedSystem<I, O>) -> SystemId<I, O>
173    where
174        I: SystemInput + 'static,
175        O: 'static,
176    {
177        let entity = self.spawn(RegisteredSystem::new(system)).id();
178        SystemId::from_entity(entity)
179    }
180
181    /// Removes a registered system and returns the system, if it exists.
182    /// After removing a system, the [`SystemId`] becomes invalid and attempting to use it afterwards will result in errors.
183    /// Re-adding the removed system will register it on a new [`SystemId`].
184    ///
185    /// If no system corresponds to the given [`SystemId`], this method returns an error.
186    /// Systems are also not allowed to remove themselves, this returns an error too.
187    pub fn unregister_system<I, O>(
188        &mut self,
189        id: SystemId<I, O>,
190    ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
191    where
192        I: SystemInput + 'static,
193        O: 'static,
194    {
195        match self.get_entity_mut(id.entity) {
196            Ok(mut entity) => {
197                let registered_system = entity
198                    .take::<RegisteredSystem<I, O>>()
199                    .ok_or(RegisteredSystemError::SelfRemove(id))?;
200                entity.despawn();
201                Ok(RemovedSystem {
202                    initialized: registered_system.initialized,
203                    system: registered_system.system,
204                })
205            }
206            Err(_) => Err(RegisteredSystemError::SystemIdNotRegistered(id)),
207        }
208    }
209
210    /// Run stored systems by their [`SystemId`].
211    /// Before running a system, it must first be registered.
212    /// The method [`World::register_system`] stores a given system and returns a [`SystemId`].
213    /// This is different from [`RunSystemOnce::run_system_once`](crate::system::RunSystemOnce::run_system_once),
214    /// because it keeps local state between calls and change detection works correctly.
215    ///
216    /// Also runs any queued-up commands.
217    ///
218    /// In order to run a chained system with an input, use [`World::run_system_with`] instead.
219    ///
220    /// # Examples
221    ///
222    /// ## Running a system
223    ///
224    /// ```
225    /// # use bevy_ecs::prelude::*;
226    /// fn increment(mut counter: Local<u8>) {
227    ///    *counter += 1;
228    ///    println!("{}", *counter);
229    /// }
230    ///
231    /// let mut world = World::default();
232    /// let counter_one = world.register_system(increment);
233    /// let counter_two = world.register_system(increment);
234    /// world.run_system(counter_one); // -> 1
235    /// world.run_system(counter_one); // -> 2
236    /// world.run_system(counter_two); // -> 1
237    /// ```
238    ///
239    /// ## Change detection
240    ///
241    /// ```
242    /// # use bevy_ecs::prelude::*;
243    /// #[derive(Resource, Default)]
244    /// struct ChangeDetector;
245    ///
246    /// let mut world = World::default();
247    /// world.init_resource::<ChangeDetector>();
248    /// let detector = world.register_system(|change_detector: ResMut<ChangeDetector>| {
249    ///     if change_detector.is_changed() {
250    ///         println!("Something happened!");
251    ///     } else {
252    ///         println!("Nothing happened.");
253    ///     }
254    /// });
255    ///
256    /// // Resources are changed when they are first added
257    /// let _ = world.run_system(detector); // -> Something happened!
258    /// let _ = world.run_system(detector); // -> Nothing happened.
259    /// world.resource_mut::<ChangeDetector>().set_changed();
260    /// let _ = world.run_system(detector); // -> Something happened!
261    /// ```
262    ///
263    /// ## Getting system output
264    ///
265    /// ```
266    /// # use bevy_ecs::prelude::*;
267    ///
268    /// #[derive(Resource)]
269    /// struct PlayerScore(i32);
270    ///
271    /// #[derive(Resource)]
272    /// struct OpponentScore(i32);
273    ///
274    /// fn get_player_score(player_score: Res<PlayerScore>) -> i32 {
275    ///   player_score.0
276    /// }
277    ///
278    /// fn get_opponent_score(opponent_score: Res<OpponentScore>) -> i32 {
279    ///   opponent_score.0
280    /// }
281    ///
282    /// let mut world = World::default();
283    /// world.insert_resource(PlayerScore(3));
284    /// world.insert_resource(OpponentScore(2));
285    ///
286    /// let scoring_systems = [
287    ///   ("player", world.register_system(get_player_score)),
288    ///   ("opponent", world.register_system(get_opponent_score)),
289    /// ];
290    ///
291    /// for (label, scoring_system) in scoring_systems {
292    ///   println!("{label} has score {}", world.run_system(scoring_system).expect("system succeeded"));
293    /// }
294    /// ```
295    pub fn run_system<O: 'static>(
296        &mut self,
297        id: SystemId<(), O>,
298    ) -> Result<O, RegisteredSystemError<(), O>> {
299        self.run_system_with(id, ())
300    }
301
302    /// Run a stored chained system by its [`SystemId`], providing an input value.
303    /// Before running a system, it must first be registered.
304    /// The method [`World::register_system`] stores a given system and returns a [`SystemId`].
305    ///
306    /// Also runs any queued-up commands.
307    ///
308    /// # Examples
309    ///
310    /// ```
311    /// # use bevy_ecs::prelude::*;
312    /// fn increment(In(increment_by): In<u8>, mut counter: Local<u8>) -> u8 {
313    ///   *counter += increment_by;
314    ///   *counter
315    /// }
316    ///
317    /// let mut world = World::default();
318    /// let counter_one = world.register_system(increment);
319    /// let counter_two = world.register_system(increment);
320    /// assert_eq!(world.run_system_with(counter_one, 1).unwrap(), 1);
321    /// assert_eq!(world.run_system_with(counter_one, 20).unwrap(), 21);
322    /// assert_eq!(world.run_system_with(counter_two, 30).unwrap(), 30);
323    /// ```
324    ///
325    /// See [`World::run_system`] for more examples.
326    pub fn run_system_with<I, O>(
327        &mut self,
328        id: SystemId<I, O>,
329        input: I::Inner<'_>,
330    ) -> Result<O, RegisteredSystemError<I, O>>
331    where
332        I: SystemInput + 'static,
333        O: 'static,
334    {
335        // Lookup
336        let mut entity = self
337            .get_entity_mut(id.entity)
338            .map_err(|_| RegisteredSystemError::SystemIdNotRegistered(id))?;
339
340        // Take ownership of system trait object
341        let RegisteredSystem {
342            mut initialized,
343            mut system,
344        } = entity
345            .take::<RegisteredSystem<I, O>>()
346            .ok_or(RegisteredSystemError::Recursive(id))?;
347
348        // Run the system
349        if !initialized {
350            system.initialize(self);
351            initialized = true;
352        }
353
354        let result = system
355            .validate_param(self)
356            .map_err(|err| RegisteredSystemError::InvalidParams { system: id, err })
357            .map(|()| {
358                // Wait to run the commands until the system is available again.
359                // This is needed so the systems can recursively run themselves.
360                let ret = system.run_without_applying_deferred(input, self);
361                system.queue_deferred(self.into());
362                ret
363            });
364
365        // Return ownership of system trait object (if entity still exists)
366        if let Ok(mut entity) = self.get_entity_mut(id.entity) {
367            entity.insert::<RegisteredSystem<I, O>>(RegisteredSystem {
368                initialized,
369                system,
370            });
371        }
372
373        // Run any commands enqueued by the system
374        self.flush();
375        result
376    }
377
378    /// Registers a system or returns its cached [`SystemId`].
379    ///
380    /// If you want to run the system immediately and you don't need its `SystemId`, see
381    /// [`World::run_system_cached`].
382    ///
383    /// The first time this function is called for a particular system, it will register it and
384    /// store its [`SystemId`] in a [`CachedSystemId`] resource for later. If you would rather
385    /// manage the `SystemId` yourself, or register multiple copies of the same system, use
386    /// [`World::register_system`] instead.
387    ///
388    /// # Limitations
389    ///
390    /// This function only accepts ZST (zero-sized) systems to guarantee that any two systems of
391    /// the same type must be equal. This means that closures that capture the environment, and
392    /// function pointers, are not accepted.
393    ///
394    /// If you want to access values from the environment within a system, consider passing them in
395    /// as inputs via [`World::run_system_cached_with`]. If that's not an option, consider
396    /// [`World::register_system`] instead.
397    pub fn register_system_cached<I, O, M, S>(&mut self, system: S) -> SystemId<I, O>
398    where
399        I: SystemInput + 'static,
400        O: 'static,
401        S: IntoSystem<I, O, M> + 'static,
402    {
403        const {
404            assert!(
405                size_of::<S>() == 0,
406                "Non-ZST systems (e.g. capturing closures, function pointers) cannot be cached.",
407            );
408        }
409
410        if !self.contains_resource::<CachedSystemId<S>>() {
411            let id = self.register_system(system);
412            self.insert_resource(CachedSystemId::<S>::new(id));
413            return id;
414        }
415
416        self.resource_scope(|world, mut id: Mut<CachedSystemId<S>>| {
417            if let Ok(mut entity) = world.get_entity_mut(id.entity) {
418                if !entity.contains::<RegisteredSystem<I, O>>() {
419                    entity.insert(RegisteredSystem::new(Box::new(IntoSystem::into_system(
420                        system,
421                    ))));
422                }
423            } else {
424                id.entity = world.register_system(system).entity();
425            }
426            SystemId::from_entity(id.entity)
427        })
428    }
429
430    /// Removes a cached system and its [`CachedSystemId`] resource.
431    ///
432    /// See [`World::register_system_cached`] for more information.
433    pub fn unregister_system_cached<I, O, M, S>(
434        &mut self,
435        _system: S,
436    ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
437    where
438        I: SystemInput + 'static,
439        O: 'static,
440        S: IntoSystem<I, O, M> + 'static,
441    {
442        let id = self
443            .remove_resource::<CachedSystemId<S>>()
444            .ok_or(RegisteredSystemError::SystemNotCached)?;
445        self.unregister_system(SystemId::<I, O>::from_entity(id.entity))
446    }
447
448    /// Runs a cached system, registering it if necessary.
449    ///
450    /// See [`World::register_system_cached`] for more information.
451    pub fn run_system_cached<O: 'static, M, S: IntoSystem<(), O, M> + 'static>(
452        &mut self,
453        system: S,
454    ) -> Result<O, RegisteredSystemError<(), O>> {
455        self.run_system_cached_with(system, ())
456    }
457
458    /// Runs a cached system with an input, registering it if necessary.
459    ///
460    /// See [`World::register_system_cached`] for more information.
461    pub fn run_system_cached_with<I, O, M, S>(
462        &mut self,
463        system: S,
464        input: I::Inner<'_>,
465    ) -> Result<O, RegisteredSystemError<I, O>>
466    where
467        I: SystemInput + 'static,
468        O: 'static,
469        S: IntoSystem<I, O, M> + 'static,
470    {
471        let id = self.register_system_cached(system);
472        self.run_system_with(id, input)
473    }
474}
475
476/// An operation with stored systems failed.
477#[derive(Error)]
478pub enum RegisteredSystemError<I: SystemInput = (), O = ()> {
479    /// A system was run by id, but no system with that id was found.
480    ///
481    /// Did you forget to register it?
482    #[error("System {0:?} was not registered")]
483    SystemIdNotRegistered(SystemId<I, O>),
484    /// A cached system was removed by value, but no system with its type was found.
485    ///
486    /// Did you forget to register it?
487    #[error("Cached system was not found")]
488    SystemNotCached,
489    /// A system tried to run itself recursively.
490    #[error("System {0:?} tried to run itself recursively")]
491    Recursive(SystemId<I, O>),
492    /// A system tried to remove itself.
493    #[error("System {0:?} tried to remove itself")]
494    SelfRemove(SystemId<I, O>),
495    /// System could not be run due to parameters that failed validation.
496    /// This should not be considered an error if [`field@SystemParamValidationError::skipped`] is `true`.
497    #[error("System {system:?} did not run due to failed parameter validation: {err}")]
498    InvalidParams {
499        /// The identifier of the system that was run.
500        system: SystemId<I, O>,
501        /// The returned parameter validation error.
502        err: SystemParamValidationError,
503    },
504}
505
506impl<I: SystemInput, O> core::fmt::Debug for RegisteredSystemError<I, O> {
507    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
508        match self {
509            Self::SystemIdNotRegistered(arg0) => {
510                f.debug_tuple("SystemIdNotRegistered").field(arg0).finish()
511            }
512            Self::SystemNotCached => write!(f, "SystemNotCached"),
513            Self::Recursive(arg0) => f.debug_tuple("Recursive").field(arg0).finish(),
514            Self::SelfRemove(arg0) => f.debug_tuple("SelfRemove").field(arg0).finish(),
515            Self::InvalidParams { system, err } => f
516                .debug_struct("InvalidParams")
517                .field("system", system)
518                .field("err", err)
519                .finish(),
520        }
521    }
522}
523
524#[cfg(test)]
525mod tests {
526    use core::cell::Cell;
527
528    use bevy_utils::default;
529
530    use crate::{prelude::*, system::SystemId};
531
532    #[derive(Resource, Default, PartialEq, Debug)]
533    struct Counter(u8);
534
535    #[test]
536    fn change_detection() {
537        #[derive(Resource, Default)]
538        struct ChangeDetector;
539
540        fn count_up_iff_changed(
541            mut counter: ResMut<Counter>,
542            change_detector: ResMut<ChangeDetector>,
543        ) {
544            if change_detector.is_changed() {
545                counter.0 += 1;
546            }
547        }
548
549        let mut world = World::new();
550        world.init_resource::<ChangeDetector>();
551        world.init_resource::<Counter>();
552        assert_eq!(*world.resource::<Counter>(), Counter(0));
553        // Resources are changed when they are first added.
554        let id = world.register_system(count_up_iff_changed);
555        world.run_system(id).expect("system runs successfully");
556        assert_eq!(*world.resource::<Counter>(), Counter(1));
557        // Nothing changed
558        world.run_system(id).expect("system runs successfully");
559        assert_eq!(*world.resource::<Counter>(), Counter(1));
560        // Making a change
561        world.resource_mut::<ChangeDetector>().set_changed();
562        world.run_system(id).expect("system runs successfully");
563        assert_eq!(*world.resource::<Counter>(), Counter(2));
564    }
565
566    #[test]
567    fn local_variables() {
568        // The `Local` begins at the default value of 0
569        fn doubling(last_counter: Local<Counter>, mut counter: ResMut<Counter>) {
570            counter.0 += last_counter.0 .0;
571            last_counter.0 .0 = counter.0;
572        }
573
574        let mut world = World::new();
575        world.insert_resource(Counter(1));
576        assert_eq!(*world.resource::<Counter>(), Counter(1));
577        let id = world.register_system(doubling);
578        world.run_system(id).expect("system runs successfully");
579        assert_eq!(*world.resource::<Counter>(), Counter(1));
580        world.run_system(id).expect("system runs successfully");
581        assert_eq!(*world.resource::<Counter>(), Counter(2));
582        world.run_system(id).expect("system runs successfully");
583        assert_eq!(*world.resource::<Counter>(), Counter(4));
584        world.run_system(id).expect("system runs successfully");
585        assert_eq!(*world.resource::<Counter>(), Counter(8));
586    }
587
588    #[test]
589    fn input_values() {
590        // Verify that a non-Copy, non-Clone type can be passed in.
591        struct NonCopy(u8);
592
593        fn increment_sys(In(NonCopy(increment_by)): In<NonCopy>, mut counter: ResMut<Counter>) {
594            counter.0 += increment_by;
595        }
596
597        let mut world = World::new();
598
599        let id = world.register_system(increment_sys);
600
601        // Insert the resource after registering the system.
602        world.insert_resource(Counter(1));
603        assert_eq!(*world.resource::<Counter>(), Counter(1));
604
605        world
606            .run_system_with(id, NonCopy(1))
607            .expect("system runs successfully");
608        assert_eq!(*world.resource::<Counter>(), Counter(2));
609
610        world
611            .run_system_with(id, NonCopy(1))
612            .expect("system runs successfully");
613        assert_eq!(*world.resource::<Counter>(), Counter(3));
614
615        world
616            .run_system_with(id, NonCopy(20))
617            .expect("system runs successfully");
618        assert_eq!(*world.resource::<Counter>(), Counter(23));
619
620        world
621            .run_system_with(id, NonCopy(1))
622            .expect("system runs successfully");
623        assert_eq!(*world.resource::<Counter>(), Counter(24));
624    }
625
626    #[test]
627    fn output_values() {
628        // Verify that a non-Copy, non-Clone type can be returned.
629        #[derive(Eq, PartialEq, Debug)]
630        struct NonCopy(u8);
631
632        fn increment_sys(mut counter: ResMut<Counter>) -> NonCopy {
633            counter.0 += 1;
634            NonCopy(counter.0)
635        }
636
637        let mut world = World::new();
638
639        let id = world.register_system(increment_sys);
640
641        // Insert the resource after registering the system.
642        world.insert_resource(Counter(1));
643        assert_eq!(*world.resource::<Counter>(), Counter(1));
644
645        let output = world.run_system(id).expect("system runs successfully");
646        assert_eq!(*world.resource::<Counter>(), Counter(2));
647        assert_eq!(output, NonCopy(2));
648
649        let output = world.run_system(id).expect("system runs successfully");
650        assert_eq!(*world.resource::<Counter>(), Counter(3));
651        assert_eq!(output, NonCopy(3));
652    }
653
654    #[test]
655    fn exclusive_system() {
656        let mut world = World::new();
657        let exclusive_system_id = world.register_system(|world: &mut World| {
658            world.spawn_empty();
659        });
660        let entity_count = world.entities.len();
661        let _ = world.run_system(exclusive_system_id);
662        assert_eq!(world.entities.len(), entity_count + 1);
663    }
664
665    #[test]
666    fn nested_systems() {
667        use crate::system::SystemId;
668
669        #[derive(Component)]
670        struct Callback(SystemId);
671
672        fn nested(query: Query<&Callback>, mut commands: Commands) {
673            for callback in query.iter() {
674                commands.run_system(callback.0);
675            }
676        }
677
678        let mut world = World::new();
679        world.insert_resource(Counter(0));
680
681        let increment_two = world.register_system(|mut counter: ResMut<Counter>| {
682            counter.0 += 2;
683        });
684        let increment_three = world.register_system(|mut counter: ResMut<Counter>| {
685            counter.0 += 3;
686        });
687        let nested_id = world.register_system(nested);
688
689        world.spawn(Callback(increment_two));
690        world.spawn(Callback(increment_three));
691        let _ = world.run_system(nested_id);
692        assert_eq!(*world.resource::<Counter>(), Counter(5));
693    }
694
695    #[test]
696    fn nested_systems_with_inputs() {
697        use crate::system::SystemId;
698
699        #[derive(Component)]
700        struct Callback(SystemId<In<u8>>, u8);
701
702        fn nested(query: Query<&Callback>, mut commands: Commands) {
703            for callback in query.iter() {
704                commands.run_system_with(callback.0, callback.1);
705            }
706        }
707
708        let mut world = World::new();
709        world.insert_resource(Counter(0));
710
711        let increment_by =
712            world.register_system(|In(amt): In<u8>, mut counter: ResMut<Counter>| {
713                counter.0 += amt;
714            });
715        let nested_id = world.register_system(nested);
716
717        world.spawn(Callback(increment_by, 2));
718        world.spawn(Callback(increment_by, 3));
719        let _ = world.run_system(nested_id);
720        assert_eq!(*world.resource::<Counter>(), Counter(5));
721    }
722
723    #[test]
724    fn cached_system() {
725        use crate::system::RegisteredSystemError;
726
727        fn four() -> i32 {
728            4
729        }
730
731        let mut world = World::new();
732        let old = world.register_system_cached(four);
733        let new = world.register_system_cached(four);
734        assert_eq!(old, new);
735
736        let result = world.unregister_system_cached(four);
737        assert!(result.is_ok());
738        let new = world.register_system_cached(four);
739        assert_ne!(old, new);
740
741        let output = world.run_system(old);
742        assert!(matches!(
743            output,
744            Err(RegisteredSystemError::SystemIdNotRegistered(x)) if x == old,
745        ));
746        let output = world.run_system(new);
747        assert!(matches!(output, Ok(x) if x == four()));
748        let output = world.run_system_cached(four);
749        assert!(matches!(output, Ok(x) if x == four()));
750        let output = world.run_system_cached_with(four, ());
751        assert!(matches!(output, Ok(x) if x == four()));
752    }
753
754    #[test]
755    fn cached_system_commands() {
756        fn sys(mut counter: ResMut<Counter>) {
757            counter.0 = 1;
758        }
759
760        let mut world = World::new();
761        world.insert_resource(Counter(0));
762
763        world.commands().run_system_cached(sys);
764        world.flush_commands();
765
766        assert_eq!(world.resource::<Counter>().0, 1);
767    }
768
769    #[test]
770    fn cached_system_adapters() {
771        fn four() -> i32 {
772            4
773        }
774
775        fn double(In(i): In<i32>) -> i32 {
776            i * 2
777        }
778
779        let mut world = World::new();
780
781        let output = world.run_system_cached(four.pipe(double));
782        assert!(matches!(output, Ok(8)));
783
784        let output = world.run_system_cached(four.map(|i| i * 2));
785        assert!(matches!(output, Ok(8)));
786    }
787
788    #[test]
789    fn cached_system_into_same_system_type() {
790        use crate::error::Result;
791
792        struct Foo;
793        impl IntoSystem<(), Result<()>, ()> for Foo {
794            type System = ApplyDeferred;
795            fn into_system(_: Self) -> Self::System {
796                ApplyDeferred
797            }
798        }
799
800        struct Bar;
801        impl IntoSystem<(), Result<()>, ()> for Bar {
802            type System = ApplyDeferred;
803            fn into_system(_: Self) -> Self::System {
804                ApplyDeferred
805            }
806        }
807
808        let mut world = World::new();
809        let foo1 = world.register_system_cached(Foo);
810        let foo2 = world.register_system_cached(Foo);
811        let bar1 = world.register_system_cached(Bar);
812        let bar2 = world.register_system_cached(Bar);
813
814        // The `S: IntoSystem` types are different, so they should be cached
815        // as separate systems, even though the `<S as IntoSystem>::System`
816        // types / values are the same (`ApplyDeferred`).
817        assert_ne!(foo1, bar1);
818
819        // But if the `S: IntoSystem` types are the same, they'll be cached
820        // as the same system.
821        assert_eq!(foo1, foo2);
822        assert_eq!(bar1, bar2);
823    }
824
825    #[test]
826    fn system_with_input_ref() {
827        fn with_ref(InRef(input): InRef<u8>, mut counter: ResMut<Counter>) {
828            counter.0 += *input;
829        }
830
831        let mut world = World::new();
832        world.insert_resource(Counter(0));
833
834        let id = world.register_system(with_ref);
835        world.run_system_with(id, &2).unwrap();
836        assert_eq!(*world.resource::<Counter>(), Counter(2));
837    }
838
839    #[test]
840    fn system_with_input_mut() {
841        #[derive(Event)]
842        struct MyEvent {
843            cancelled: bool,
844        }
845
846        fn post(InMut(event): InMut<MyEvent>, counter: ResMut<Counter>) {
847            if counter.0 > 0 {
848                event.cancelled = true;
849            }
850        }
851
852        let mut world = World::new();
853        world.insert_resource(Counter(0));
854        let post_system = world.register_system(post);
855
856        let mut event = MyEvent { cancelled: false };
857        world.run_system_with(post_system, &mut event).unwrap();
858        assert!(!event.cancelled);
859
860        world.resource_mut::<Counter>().0 = 1;
861        world.run_system_with(post_system, &mut event).unwrap();
862        assert!(event.cancelled);
863    }
864
865    #[test]
866    fn run_system_invalid_params() {
867        use crate::system::RegisteredSystemError;
868        use alloc::{format, string::ToString};
869
870        struct T;
871        impl Resource for T {}
872        fn system(_: Res<T>) {}
873
874        let mut world = World::new();
875        let id = world.register_system(system);
876        // This fails because `T` has not been added to the world yet.
877        let result = world.run_system(id);
878
879        assert!(matches!(
880            result,
881            Err(RegisteredSystemError::InvalidParams { .. })
882        ));
883        let expected = format!("System {id:?} did not run due to failed parameter validation: Parameter `Res<T>` failed validation: Resource does not exist");
884        assert_eq!(expected, result.unwrap_err().to_string());
885    }
886
887    #[test]
888    fn run_system_recursive() {
889        std::thread_local! {
890            static INVOCATIONS_LEFT: Cell<i32> = const { Cell::new(3) };
891            static SYSTEM_ID: Cell<Option<SystemId>> = default();
892        }
893
894        fn system(mut commands: Commands) {
895            let count = INVOCATIONS_LEFT.get() - 1;
896            INVOCATIONS_LEFT.set(count);
897            if count > 0 {
898                commands.run_system(SYSTEM_ID.get().unwrap());
899            }
900        }
901
902        let mut world = World::new();
903        let id = world.register_system(system);
904        SYSTEM_ID.set(Some(id));
905        world.run_system(id).unwrap();
906
907        assert_eq!(INVOCATIONS_LEFT.get(), 0);
908    }
909
910    #[test]
911    fn run_system_exclusive_adapters() {
912        let mut world = World::new();
913        fn system(_: &mut World) {}
914        world.run_system_cached(system).unwrap();
915        world.run_system_cached(system.pipe(system)).unwrap();
916        world.run_system_cached(system.map(|()| {})).unwrap();
917    }
918}