Skip to main content

bevy_ecs/system/
system_registry.rs

1#[cfg(feature = "hotpatching")]
2use crate::{change_detection::DetectChanges, HotPatchChanges};
3use crate::{
4    change_detection::Mut,
5    entity::Entity,
6    error::BevyError,
7    prelude::{FromTemplate, Template},
8    system::{
9        input::SystemInput, BoxedSystem, Commands, If, IntoSystem, Res, RunSystemError,
10        SystemParamValidationError,
11    },
12    template::TemplateContext,
13    world::World,
14};
15use alloc::boxed::Box;
16use bevy_ecs_macros::{Component, Resource};
17use bevy_platform::sync::{Arc, Mutex};
18use bevy_utils::prelude::DebugName;
19use concurrent_queue::ConcurrentQueue;
20use core::{any::TypeId, marker::PhantomData};
21use thiserror::Error;
22
23/// A small wrapper for [`BoxedSystem`] that also keeps track whether or not the system has been initialized.
24#[derive(Component)]
25#[require(SystemIdMarker = SystemIdMarker::typed_system_id_marker::<I, O>())]
26pub struct RegisteredSystem<I, O> {
27    initialized: bool,
28    system: Option<BoxedSystem<I, O>>,
29}
30
31impl<I, O> RegisteredSystem<I, O> {
32    /// Create an uninitialized [`RegisteredSystem`] component with the provided boxed system
33    pub fn new(system: BoxedSystem<I, O>) -> Self {
34        RegisteredSystem {
35            initialized: false,
36            system: Some(system),
37        }
38    }
39}
40
41#[derive(Debug, Clone)]
42struct TypeIdAndName {
43    type_id: TypeId,
44    name: DebugName,
45}
46
47impl TypeIdAndName {
48    fn new<T: 'static>() -> Self {
49        Self {
50            type_id: TypeId::of::<T>(),
51            name: DebugName::type_name::<T>(),
52        }
53    }
54}
55
56impl Default for TypeIdAndName {
57    fn default() -> Self {
58        Self {
59            type_id: TypeId::of::<()>(),
60            name: DebugName::type_name::<()>(),
61        }
62    }
63}
64
65/// Marker [`Component`](bevy_ecs::component::Component) for identifying [`SystemId`] [`Entity`]s.
66#[derive(Debug, Default, Clone, Component)]
67pub struct SystemIdMarker {
68    input_type_id: TypeIdAndName,
69    output_type_id: TypeIdAndName,
70}
71
72impl SystemIdMarker {
73    fn typed_system_id_marker<I: 'static, O: 'static>() -> Self {
74        Self {
75            input_type_id: TypeIdAndName::new::<I>(),
76            output_type_id: TypeIdAndName::new::<O>(),
77        }
78    }
79}
80
81/// A system that has been removed from the registry.
82/// It contains the system and whether or not it has been initialized.
83///
84/// This struct is returned by [`World::unregister_system`].
85pub struct RemovedSystem<I = (), O = ()> {
86    initialized: bool,
87    system: BoxedSystem<I, O>,
88}
89
90impl<I, O> RemovedSystem<I, O> {
91    /// Is the system initialized?
92    /// A system is initialized the first time it's ran.
93    pub fn initialized(&self) -> bool {
94        self.initialized
95    }
96
97    /// The system removed from the storage.
98    pub fn system(self) -> BoxedSystem<I, O> {
99        self.system
100    }
101}
102
103/// A system that despawns any registered system entities whose [`SystemHandle`]
104/// reference count has reached zero.
105pub fn despawn_unused_registered_systems(
106    // `RegisteredSystemDespawner` is initialized lazily the first time a system
107    // is registered, so it's possible that it doesn't exist yet when this system runs.
108    despawner: If<Res<RegisteredSystemDespawner>>,
109    mut commands: Commands,
110) {
111    for entity in despawner.queue.try_iter() {
112        // In case the entity was already despawned manually, we ignore the error here.
113        commands.entity(entity).try_despawn();
114    }
115}
116
117/// A resource that stores the channel for despawning unused registered system
118/// entities.
119#[derive(Resource)]
120pub struct RegisteredSystemDespawner {
121    queue: Arc<ConcurrentQueue<Entity>>,
122}
123
124impl Default for RegisteredSystemDespawner {
125    fn default() -> Self {
126        Self {
127            queue: Arc::new(ConcurrentQueue::unbounded()),
128        }
129    }
130}
131
132/// A maybe-strong handle to an entity acting as a registered system. Strong
133/// handles are created by [`World::register_tracked_system`] or
134/// [`World::register_tracked_boxed_system`].
135///
136/// Strong handles provide automatic cleanup of registered systems once all clones
137/// of the handle are dropped, while weak handles do not. However, the **existence
138/// of a strong handle does not prevent the registered system entity from being
139/// despawned manually**, like with [`World::unregister_system`] or
140/// [`World::unregister_system_cached`].
141///
142/// # Cleanup
143///
144/// Registered system entities are cleaned up by the [`despawn_unused_registered_systems`]
145/// system, which is automatically added to the default app by the `bevy_app`
146/// crate when the "std" feature is enabled. If not using the default app, the
147/// "std" feature, or `bevy_app` in general, consider running this system
148/// yourself to ensure proper cleanup of registered systems.
149pub enum SystemHandle<I: SystemInput = (), O = ()> {
150    /// A strong handle keeps the system entity alive as long as the handle
151    /// (and any clones of it) exist, as long as the system entity isn't
152    /// manually despawned.
153    Strong(Arc<StrongSystemHandle>),
154    /// A weak handle does not keep the system entity alive.
155    Weak(SystemId<I, O>),
156}
157
158impl<I: SystemInput, O> SystemHandle<I, O> {
159    /// Returns the [`Entity`] of the registered system associated with this handle.
160    pub fn entity(&self) -> Entity {
161        match self {
162            SystemHandle::Strong(strong) => strong.entity,
163            SystemHandle::Weak(weak) => weak.entity,
164        }
165    }
166}
167
168impl<I: SystemInput, O> Eq for SystemHandle<I, O> {}
169
170// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
171impl<I: SystemInput, O> Clone for SystemHandle<I, O> {
172    fn clone(&self) -> Self {
173        match self {
174            SystemHandle::Strong(strong) => SystemHandle::Strong(Arc::clone(strong)),
175            SystemHandle::Weak(weak) => SystemHandle::Weak(*weak),
176        }
177    }
178}
179
180// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters,
181// and so that strong and weak handles can be compared for equality based on their entities.
182impl<I: SystemInput, O> PartialEq for SystemHandle<I, O> {
183    fn eq(&self, other: &Self) -> bool {
184        self.entity() == other.entity()
185    }
186}
187
188impl<I: SystemInput, O> PartialEq<SystemId<I, O>> for SystemHandle<I, O> {
189    fn eq(&self, other: &SystemId<I, O>) -> bool {
190        self.entity() == other.entity
191    }
192}
193
194// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters,
195// and so that the handle can be hashed based on its entity instead of its handle type.
196impl<I: SystemInput, O> core::hash::Hash for SystemHandle<I, O> {
197    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
198        self.entity().hash(state);
199    }
200}
201
202impl<I: SystemInput, O> core::fmt::Debug for SystemHandle<I, O> {
203    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
204        let name = if matches!(self, SystemHandle::Strong(_)) {
205            "StrongSystemHandle"
206        } else {
207            "WeakSystemHandle"
208        };
209        f.debug_tuple(name).field(&self.entity()).finish()
210    }
211}
212
213impl<I: SystemInput, O> From<SystemId<I, O>> for SystemHandle<I, O> {
214    fn from(id: SystemId<I, O>) -> Self {
215        SystemHandle::Weak(id)
216    }
217}
218
219/// A strong handle for a registered system that despawns the entity when dropped.
220pub struct StrongSystemHandle {
221    entity: Entity,
222    drop_queue: Arc<ConcurrentQueue<Entity>>,
223}
224
225impl Drop for StrongSystemHandle {
226    fn drop(&mut self) {
227        // Send the entity to be despawned by the world when the last strong handle is dropped.
228        let _ = self.drop_queue.push(self.entity);
229    }
230}
231
232/// An identifier for a registered system.
233///
234/// These are opaque identifiers, keyed to a specific [`World`],
235/// and are created via [`World::register_system`].
236pub struct SystemId<I: SystemInput = (), O = ()> {
237    pub(crate) entity: Entity,
238    pub(crate) marker: PhantomData<fn(I) -> O>,
239}
240
241impl<I: SystemInput, O> SystemId<I, O> {
242    /// Transforms a [`SystemId`] into the [`Entity`] that holds the one-shot system's state.
243    ///
244    /// It's trivial to convert [`SystemId`] into an [`Entity`] since a one-shot system
245    /// is really an entity with associated handler function.
246    ///
247    /// For example, this is useful if you want to assign a name label to a system.
248    pub fn entity(self) -> Entity {
249        self.entity
250    }
251
252    /// Create [`SystemId`] from an [`Entity`]. Useful when you only have entity handles to avoid
253    /// adding extra components that have a [`SystemId`] everywhere. To run a system with this ID
254    ///  - The entity must be a system
255    ///  - The `I` + `O` types must be correct
256    pub fn from_entity(entity: Entity) -> Self {
257        Self {
258            entity,
259            marker: PhantomData,
260        }
261    }
262}
263
264impl<I: SystemInput, O> Eq for SystemId<I, O> {}
265
266// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
267impl<I: SystemInput, O> Copy for SystemId<I, O> {}
268
269// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
270impl<I: SystemInput, O> Clone for SystemId<I, O> {
271    fn clone(&self) -> Self {
272        *self
273    }
274}
275
276// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
277impl<I: SystemInput, O> PartialEq for SystemId<I, O> {
278    fn eq(&self, other: &Self) -> bool {
279        self.entity == other.entity && self.marker == other.marker
280    }
281}
282
283// A manual impl is used because the trait bounds should ignore the `I` and `O` phantom parameters.
284impl<I: SystemInput, O> core::hash::Hash for SystemId<I, O> {
285    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
286        self.entity.hash(state);
287    }
288}
289
290impl<I: SystemInput, O> core::fmt::Debug for SystemId<I, O> {
291    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
292        f.debug_tuple("SystemId").field(&self.entity).finish()
293    }
294}
295
296impl<I: SystemInput, O> From<&SystemHandle<I, O>> for SystemId<I, O> {
297    fn from(handle: &SystemHandle<I, O>) -> Self {
298        Self::from_entity(handle.entity())
299    }
300}
301
302impl<I: SystemInput, O> From<SystemHandle<I, O>> for SystemId<I, O> {
303    fn from(handle: SystemHandle<I, O>) -> Self {
304        (&handle).into()
305    }
306}
307
308impl<I: SystemInput + 'static, O: 'static> FromTemplate for SystemHandle<I, O> {
309    type Template = SystemHandleTemplate<I, O>;
310}
311
312/// A [`Template`] that produces a [`SystemHandle`].
313pub enum SystemHandleTemplate<I: SystemInput + 'static = (), O: 'static = ()> {
314    /// Creates a [`SystemHandle`] by cloning the given [`SystemHandle`] value.
315    Handle(SystemHandle<I, O>),
316    /// Creates a [`SystemHandle`] by registering the given system value using
317    /// [`World::register_tracked_boxed_system`]. This will cache the resulting
318    /// [`SystemHandle`]
319    /// on the template and reuse it for future template builds.
320    ///
321    /// This should generally be constructed using [`SystemHandleTemplate::value`]
322    /// or [`system_value`].
323    Value(SystemHandleValue<I, O>),
324}
325
326/// Stores an [`Arc<Mutex<SystemHandleOrValue<I, O>>>`].
327pub struct SystemHandleValue<I: SystemInput + 'static = (), O: 'static = ()>(
328    Arc<Mutex<SystemHandleOrValue<I, O>>>,
329);
330
331impl<I: SystemInput + 'static, O: 'static> Clone for SystemHandleValue<I, O> {
332    fn clone(&self) -> Self {
333        Self(Arc::clone(&self.0))
334    }
335}
336
337enum SystemHandleOrValue<I: SystemInput + 'static = (), O: 'static = ()> {
338    Handle(SystemHandle<I, O>),
339    Value(Option<BoxedSystem<I, O>>),
340}
341
342impl<I: SystemInput + 'static, O: 'static> SystemHandleTemplate<I, O> {
343    /// This will create a new [`SystemHandleTemplate`] for the given `system` value.
344    /// This makes it possible to define systems "inline" in templates / scenes
345    /// that produce a [`SystemId`].
346    pub fn value<M>(system: impl IntoSystem<I, O, M>) -> Self {
347        Self::Value(SystemHandleValue(Arc::new(Mutex::new(
348            SystemHandleOrValue::Value(Some(Box::new(IntoSystem::into_system(system)))),
349        ))))
350    }
351}
352
353impl<I: SystemInput + 'static, O: 'static> Template for SystemHandleTemplate<I, O> {
354    type Output = SystemHandle<I, O>;
355
356    fn build_template(
357        &self,
358        context: &mut TemplateContext,
359    ) -> crate::prelude::Result<Self::Output> {
360        match self {
361            Self::Handle(handle) => Ok(handle.clone()),
362            Self::Value(value) => {
363                let mut value_or_id = value.0.lock().unwrap();
364                match &mut *value_or_id {
365                    SystemHandleOrValue::Handle(handle) => Ok(handle.clone()),
366                    SystemHandleOrValue::Value(system) => {
367                        let system = system.take().unwrap();
368                        let id = context
369                            .entity
370                            .world_scope(|world| world.register_tracked_boxed_system(system));
371                        *value_or_id = SystemHandleOrValue::Handle(id.clone());
372                        Ok(id)
373                    }
374                }
375            }
376        }
377    }
378
379    fn clone_template(&self) -> Self {
380        match self {
381            Self::Handle(handle) => Self::Handle(handle.clone()),
382            Self::Value(value) => Self::Value(value.clone()),
383        }
384    }
385}
386
387impl<I: SystemInput + 'static, O: 'static> Default for SystemHandleTemplate<I, O> {
388    fn default() -> Self {
389        Self::Handle(SystemHandle::Weak(SystemId::from_entity(
390            Entity::PLACEHOLDER,
391        )))
392    }
393}
394
395impl<I: SystemInput + 'static, O: 'static> From<SystemHandle<I, O>> for SystemHandleTemplate<I, O> {
396    fn from(handle: SystemHandle<I, O>) -> Self {
397        Self::Handle(handle)
398    }
399}
400
401impl<I: SystemInput + 'static, O: 'static> From<BoxedSystem<I, O>> for SystemHandleTemplate<I, O> {
402    fn from(system: BoxedSystem<I, O>) -> Self {
403        Self::Value(SystemHandleValue(Arc::new(Mutex::new(
404            SystemHandleOrValue::Value(Some(system)),
405        ))))
406    }
407}
408
409impl<I: SystemInput + 'static, O: 'static> From<SystemId<I, O>> for SystemHandleTemplate<I, O> {
410    fn from(id: SystemId<I, O>) -> Self {
411        Self::Handle(SystemHandle::Weak(id))
412    }
413}
414
415/// This will create a new [`SystemHandleTemplate`] for the given `system` value.
416/// This makes it possible to define systems "inline" in templates / scenes that
417/// produce a [`SystemHandle`].
418pub fn system_value<I: SystemInput + 'static, O: 'static, M>(
419    system: impl IntoSystem<I, O, M>,
420) -> SystemHandleTemplate<I, O> {
421    SystemHandleTemplate::value(system)
422}
423
424/// A cached [`SystemId`] distinguished by the unique function type of its system.
425///
426/// This resource is inserted by [`World::register_system_cached`].
427#[derive(Resource)]
428pub struct CachedSystemId<S> {
429    /// The cached `SystemId` as an `Entity`.
430    pub entity: Entity,
431    _marker: PhantomData<fn() -> S>,
432}
433
434impl<S> CachedSystemId<S> {
435    /// Creates a new `CachedSystemId` struct given a `SystemId`.
436    pub fn new<I: SystemInput, O>(id: SystemId<I, O>) -> Self {
437        Self {
438            entity: id.entity(),
439            _marker: PhantomData,
440        }
441    }
442}
443
444impl World {
445    /// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`].
446    ///
447    /// It's possible to register multiple copies of the same system by calling this function
448    /// multiple times. If that's not what you want, consider using [`World::register_system_cached`]
449    /// instead.
450    ///
451    /// This is different from adding systems to a [`Schedule`](crate::schedule::Schedule),
452    /// because the [`SystemId`] that is returned can be used anywhere in the [`World`] to run the associated system.
453    /// This allows for running systems in a pushed-based fashion.
454    /// Using a [`Schedule`](crate::schedule::Schedule) is still preferred for most cases
455    /// due to its better performance and ability to run non-conflicting systems simultaneously.
456    pub fn register_system<I, O, M>(
457        &mut self,
458        system: impl IntoSystem<I, O, M> + 'static,
459    ) -> SystemId<I, O>
460    where
461        I: SystemInput + 'static,
462        O: 'static,
463    {
464        self.register_boxed_system(Box::new(IntoSystem::into_system(system)))
465    }
466
467    /// Similar to [`Self::register_system`], but allows passing in a [`BoxedSystem`].
468    ///
469    ///  This is useful if the [`IntoSystem`] implementor has already been turned into a
470    /// [`System`](crate::system::System) trait object and put in a [`Box`].
471    pub fn register_boxed_system<I, O>(&mut self, system: BoxedSystem<I, O>) -> SystemId<I, O>
472    where
473        I: SystemInput + 'static,
474        O: 'static,
475    {
476        let entity = self.spawn(RegisteredSystem::new(system)).id();
477        SystemId::from_entity(entity)
478    }
479
480    /// Registers a system and returns a tracked [`SystemHandle`] so it can later
481    /// be called by [`World::run_system`]. The system entity will be automatically
482    /// queued for despawn when the last clone of the returned handle is dropped.
483    ///
484    /// By default, unused tracked system entities are despawned by the
485    /// [`despawn_unused_registered_systems`] system in the `Last` schedule of
486    /// the default app. Otherwise, it needs to be run manually to ensure proper
487    /// cleanup of registered systems.
488    ///
489    /// It's possible to register multiple copies of the same system by calling
490    /// this function multiple times. If that's not what you want, consider using
491    /// [`World::register_system_cached`] instead.
492    pub fn register_tracked_system<I, O, M>(
493        &mut self,
494        system: impl IntoSystem<I, O, M> + 'static,
495    ) -> SystemHandle<I, O>
496    where
497        I: SystemInput + 'static,
498        O: 'static,
499    {
500        self.register_tracked_boxed_system(Box::new(IntoSystem::into_system(system)))
501    }
502
503    /// Similar to [`Self::register_tracked_system`], but allows passing in a
504    /// [`BoxedSystem`].
505    ///
506    /// This is useful if the [`IntoSystem`] implementor has already been turned
507    /// into a [`System`](crate::system::System) trait object and put in a [`Box`].
508    pub fn register_tracked_boxed_system<I, O>(
509        &mut self,
510        system: BoxedSystem<I, O>,
511    ) -> SystemHandle<I, O>
512    where
513        I: SystemInput + 'static,
514        O: 'static,
515    {
516        let entity = self.spawn(RegisteredSystem::new(system)).id();
517        let despawner = self.get_resource_or_init::<RegisteredSystemDespawner>();
518
519        SystemHandle::Strong(Arc::new(StrongSystemHandle {
520            entity,
521            drop_queue: despawner.queue.clone(),
522        }))
523    }
524
525    /// Removes a registered system and returns the system, if it exists.
526    /// After removing a system, the [`SystemId`] becomes invalid and attempting to use it afterwards will result in errors.
527    /// Re-adding the removed system will register it on a new [`SystemId`].
528    ///
529    /// If no system corresponds to the given [`SystemId`], this method returns an error.
530    /// Systems are also not allowed to remove themselves, this returns an error too.
531    pub fn unregister_system<I, O>(
532        &mut self,
533        id: SystemId<I, O>,
534    ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
535    where
536        I: SystemInput + 'static,
537        O: 'static,
538    {
539        match self.get_entity_mut(id.entity) {
540            Ok(mut entity) => {
541                let registered_system = entity
542                    .take::<RegisteredSystem<I, O>>()
543                    .ok_or(RegisteredSystemError::SelfRemove(id))?;
544                entity.despawn();
545                Ok(RemovedSystem {
546                    initialized: registered_system.initialized,
547                    system: registered_system
548                        .system
549                        .ok_or(RegisteredSystemError::SystemMissing(id))?,
550                })
551            }
552            Err(_) => Err(RegisteredSystemError::SystemIdNotRegistered(id)),
553        }
554    }
555
556    /// Run stored systems by their [`SystemId`].
557    /// Before running a system, it must first be registered.
558    /// The method [`World::register_system`] stores a given system and returns a [`SystemId`].
559    /// This is different from [`RunSystemOnce::run_system_once`](crate::system::RunSystemOnce::run_system_once),
560    /// because it keeps local state between calls and change detection works correctly.
561    ///
562    /// Also runs any queued-up commands.
563    ///
564    /// In order to run a chained system with an input, use [`World::run_system_with`] instead.
565    ///
566    /// # Examples
567    ///
568    /// ## Running a system
569    ///
570    /// ```
571    /// # use bevy_ecs::prelude::*;
572    /// fn increment(mut counter: Local<u8>) {
573    ///    *counter += 1;
574    ///    println!("{}", *counter);
575    /// }
576    ///
577    /// let mut world = World::default();
578    /// let counter_one = world.register_system(increment);
579    /// let counter_two = world.register_system(increment);
580    /// world.run_system(counter_one); // -> 1
581    /// world.run_system(counter_one); // -> 2
582    /// world.run_system(counter_two); // -> 1
583    /// ```
584    ///
585    /// ## Change detection
586    ///
587    /// ```
588    /// # use bevy_ecs::prelude::*;
589    /// #[derive(Resource, Default)]
590    /// struct ChangeDetector;
591    ///
592    /// let mut world = World::default();
593    /// world.init_resource::<ChangeDetector>();
594    /// let detector = world.register_system(|change_detector: ResMut<ChangeDetector>| {
595    ///     if change_detector.is_changed() {
596    ///         println!("Something happened!");
597    ///     } else {
598    ///         println!("Nothing happened.");
599    ///     }
600    /// });
601    ///
602    /// // Resources are changed when they are first added
603    /// let _ = world.run_system(detector); // -> Something happened!
604    /// let _ = world.run_system(detector); // -> Nothing happened.
605    /// world.resource_mut::<ChangeDetector>().set_changed();
606    /// let _ = world.run_system(detector); // -> Something happened!
607    /// ```
608    ///
609    /// ## Getting system output
610    ///
611    /// ```
612    /// # use bevy_ecs::prelude::*;
613    ///
614    /// #[derive(Resource)]
615    /// struct PlayerScore(i32);
616    ///
617    /// #[derive(Resource)]
618    /// struct OpponentScore(i32);
619    ///
620    /// fn get_player_score(player_score: Res<PlayerScore>) -> i32 {
621    ///   player_score.0
622    /// }
623    ///
624    /// fn get_opponent_score(opponent_score: Res<OpponentScore>) -> i32 {
625    ///   opponent_score.0
626    /// }
627    ///
628    /// let mut world = World::default();
629    /// world.insert_resource(PlayerScore(3));
630    /// world.insert_resource(OpponentScore(2));
631    ///
632    /// let scoring_systems = [
633    ///   ("player", world.register_system(get_player_score)),
634    ///   ("opponent", world.register_system(get_opponent_score)),
635    /// ];
636    ///
637    /// for (label, scoring_system) in scoring_systems {
638    ///   println!("{label} has score {}", world.run_system(scoring_system).expect("system succeeded"));
639    /// }
640    /// ```
641    pub fn run_system<O: 'static>(
642        &mut self,
643        id: impl Into<SystemId<(), O>>,
644    ) -> Result<O, RegisteredSystemError<(), O>> {
645        self.run_system_with(id, ())
646    }
647
648    /// Run a stored chained system by its [`SystemId`], providing an input value.
649    /// Before running a system, it must first be registered.
650    /// The method [`World::register_system`] stores a given system and returns a [`SystemId`].
651    ///
652    /// To use the supplied input, the system should have a [`SystemInput`] as the first parameter.
653    /// Also runs any queued-up commands.
654    ///
655    /// # Examples
656    ///
657    /// ```
658    /// # use bevy_ecs::prelude::*;
659    /// fn increment(In(increment_by): In<u8>, mut counter: Local<u8>) -> u8 {
660    ///   *counter += increment_by;
661    ///   *counter
662    /// }
663    ///
664    /// let mut world = World::default();
665    /// let counter_one = world.register_system(increment);
666    /// let counter_two = world.register_system(increment);
667    /// assert_eq!(world.run_system_with(counter_one, 1).unwrap(), 1);
668    /// assert_eq!(world.run_system_with(counter_one, 20).unwrap(), 21);
669    /// assert_eq!(world.run_system_with(counter_two, 30).unwrap(), 30);
670    /// ```
671    ///
672    /// See [`World::run_system`] for more examples.
673    pub fn run_system_with<I, O>(
674        &mut self,
675        id: impl Into<SystemId<I, O>>,
676        input: I::Inner<'_>,
677    ) -> Result<O, RegisteredSystemError<I, O>>
678    where
679        I: SystemInput + 'static,
680        O: 'static,
681    {
682        let id = id.into();
683        // Lookup
684        let mut entity = self
685            .get_entity_mut(id.entity)
686            .map_err(|_| RegisteredSystemError::SystemIdNotRegistered(id))?;
687
688        // Take ownership of system trait object
689        let Some(mut registered_system) = entity.get_mut::<RegisteredSystem<I, O>>() else {
690            let Some(system_id_marker) = entity.get::<SystemIdMarker>() else {
691                return Err(RegisteredSystemError::SystemIdNotRegistered(id));
692            };
693            if system_id_marker.input_type_id.type_id != TypeId::of::<I>()
694                || system_id_marker.output_type_id.type_id != TypeId::of::<O>()
695            {
696                return Err(RegisteredSystemError::IncorrectType(
697                    id,
698                    system_id_marker.clone(),
699                ));
700            }
701            return Err(RegisteredSystemError::MissingRegisteredSystemComponent(id));
702        };
703
704        let mut system = registered_system
705            .system
706            .take()
707            .ok_or(RegisteredSystemError::SystemMissing(id))?;
708
709        // Initialize the system
710        if !registered_system.initialized {
711            system.initialize(self);
712        }
713
714        // refresh hotpatches for stored systems
715        #[cfg(feature = "hotpatching")]
716        if self
717            .get_resource_ref::<HotPatchChanges>()
718            .is_none_or(|r| r.is_changed_after(system.get_last_run()))
719        {
720            system.refresh_hotpatch();
721        }
722
723        // Wait to run the commands until the system is available again.
724        // This is needed so the systems can recursively run themselves.
725        let result = system.run_without_applying_deferred(input, self);
726        system.queue_deferred(self.into());
727
728        // Return ownership of system trait object (if entity still exists)
729        if let Ok(mut entity) = self.get_entity_mut(id.entity)
730            && let Some(mut registered_system) = entity.get_mut::<RegisteredSystem<I, O>>()
731        {
732            registered_system.system = Some(system);
733            registered_system.initialized = true;
734        }
735
736        // Run any commands enqueued by the system
737        self.flush();
738        Ok(result?)
739    }
740
741    /// Registers a system or returns its cached [`SystemId`].
742    ///
743    /// If you want to run the system immediately and you don't need its `SystemId`, see
744    /// [`World::run_system_cached`].
745    ///
746    /// The first time this function is called for a particular system, it will register it and
747    /// store its [`SystemId`] in a [`CachedSystemId`] resource for later. If you would rather
748    /// manage the `SystemId` yourself, or register multiple copies of the same system, use
749    /// [`World::register_system`] instead.
750    ///
751    /// # Limitations
752    ///
753    /// This function only accepts ZST (zero-sized) systems to guarantee that any two systems of
754    /// the same type must be equal. This means that closures that capture the environment, and
755    /// function pointers, are not accepted.
756    ///
757    /// If you want to access values from the environment within a system, consider passing them in
758    /// as inputs via [`World::run_system_cached_with`]. If that's not an option, consider
759    /// [`World::register_system`] instead.
760    pub fn register_system_cached<I, O, M, S>(&mut self, system: S) -> SystemId<I, O>
761    where
762        I: SystemInput + 'static,
763        O: 'static,
764        S: IntoSystem<I, O, M> + 'static,
765    {
766        const {
767            assert!(
768                size_of::<S>() == 0,
769                "Non-ZST systems (e.g. capturing closures, function pointers) cannot be cached.",
770            );
771        }
772
773        if !self.contains_resource::<CachedSystemId<S>>() {
774            let id = self.register_system(system);
775            self.insert_resource(CachedSystemId::<S>::new(id));
776            return id;
777        }
778
779        self.resource_scope(|world, mut id: Mut<CachedSystemId<S>>| {
780            if let Ok(mut entity) = world.get_entity_mut(id.entity) {
781                if !entity.contains::<RegisteredSystem<I, O>>() {
782                    entity.insert(RegisteredSystem::new(Box::new(IntoSystem::into_system(
783                        system,
784                    ))));
785                }
786            } else {
787                id.entity = world.register_system(system).entity();
788            }
789            SystemId::from_entity(id.entity)
790        })
791    }
792
793    /// Removes a cached system and its [`CachedSystemId`] resource.
794    ///
795    /// See [`World::register_system_cached`] for more information.
796    pub fn unregister_system_cached<I, O, M, S>(
797        &mut self,
798        _system: S,
799    ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
800    where
801        I: SystemInput + 'static,
802        O: 'static,
803        S: IntoSystem<I, O, M> + 'static,
804    {
805        let id = self
806            .remove_resource::<CachedSystemId<S>>()
807            .ok_or(RegisteredSystemError::SystemNotCached)?;
808        self.unregister_system(SystemId::<I, O>::from_entity(id.entity))
809    }
810
811    /// Runs a cached system, registering it if necessary.
812    ///
813    /// See [`World::register_system_cached`] for more information.
814    pub fn run_system_cached<O: 'static, M, S: IntoSystem<(), O, M> + 'static>(
815        &mut self,
816        system: S,
817    ) -> Result<O, RegisteredSystemError<(), O>> {
818        self.run_system_cached_with(system, ())
819    }
820
821    /// Runs a cached system with an input, registering it if necessary.
822    ///
823    /// To use the supplied input, the system should have a [`SystemInput`] as the first parameter.
824    /// See [`World::register_system_cached`] for more information.
825    pub fn run_system_cached_with<I, O, M, S>(
826        &mut self,
827        system: S,
828        input: I::Inner<'_>,
829    ) -> Result<O, RegisteredSystemError<I, O>>
830    where
831        I: SystemInput + 'static,
832        O: 'static,
833        S: IntoSystem<I, O, M> + 'static,
834    {
835        let id = self.register_system_cached(system);
836        self.run_system_with(id, input)
837    }
838}
839
840/// An operation with stored systems failed.
841#[derive(Error)]
842pub enum RegisteredSystemError<I: SystemInput = (), O = ()> {
843    /// A system was run by id, but no system with that id was found.
844    ///
845    /// Did you forget to register it?
846    #[error("System {0:?} was not registered")]
847    SystemIdNotRegistered(SystemId<I, O>),
848    /// A cached system was removed by value, but no system with its type was found.
849    ///
850    /// Did you forget to register it?
851    #[error("Cached system was not found")]
852    SystemNotCached,
853    /// The `RegisteredSystem` component is missing.
854    #[error("System {0:?} does not have a RegisteredSystem component. This only happens if app code removed the component.")]
855    MissingRegisteredSystemComponent(SystemId<I, O>),
856    /// A system tried to remove itself.
857    #[error("System {0:?} tried to remove itself")]
858    SelfRemove(SystemId<I, O>),
859    /// System could not be run due to parameters that failed validation.
860    /// This is not considered an error.
861    #[error("System did not run due to failed parameter validation: {0}")]
862    Skipped(SystemParamValidationError),
863    /// System returned an error or failed required parameter validation.
864    #[error("System returned error: {0}")]
865    Failed(BevyError),
866    /// [`SystemId`] had different input and/or output types than [`SystemIdMarker`]
867    #[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)]
868    IncorrectType(SystemId<I, O>, SystemIdMarker),
869    /// System is not present in the `RegisteredSystem` component.
870    // TODO: We should consider using catch_unwind to protect against the panic case.
871    #[error("The system is not present in the RegisteredSystem component. This can happen if the system was called recursively or if the system panicked on the last run.")]
872    SystemMissing(SystemId<I, O>),
873}
874
875impl<I: SystemInput, O> From<RunSystemError> for RegisteredSystemError<I, O> {
876    fn from(value: RunSystemError) -> Self {
877        match value {
878            RunSystemError::Skipped(err) => Self::Skipped(err),
879            RunSystemError::Failed(err) => Self::Failed(err),
880        }
881    }
882}
883
884impl<I: SystemInput, O> core::fmt::Debug for RegisteredSystemError<I, O> {
885    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
886        match self {
887            Self::SystemIdNotRegistered(arg0) => {
888                f.debug_tuple("SystemIdNotRegistered").field(arg0).finish()
889            }
890            Self::SystemNotCached => write!(f, "SystemNotCached"),
891            Self::MissingRegisteredSystemComponent(arg0) => f
892                .debug_tuple("MissingRegisteredSystemComponent")
893                .field(arg0)
894                .finish(),
895            Self::SelfRemove(arg0) => f.debug_tuple("SelfRemove").field(arg0).finish(),
896            Self::Skipped(arg0) => f.debug_tuple("Skipped").field(arg0).finish(),
897            Self::Failed(arg0) => f.debug_tuple("Failed").field(arg0).finish(),
898            Self::IncorrectType(arg0, arg1) => f
899                .debug_tuple("IncorrectType")
900                .field(arg0)
901                .field(arg1)
902                .finish(),
903            Self::SystemMissing(arg0) => f.debug_tuple("SystemMissing").field(arg0).finish(),
904        }
905    }
906}
907
908#[cfg(test)]
909mod tests {
910    use core::cell::Cell;
911
912    use bevy_utils::default;
913
914    use crate::{
915        prelude::*,
916        system::{
917            despawn_unused_registered_systems, system_value, RegisteredSystemError, SystemHandle,
918            SystemHandleTemplate, SystemId,
919        },
920    };
921
922    #[derive(Resource, Default, PartialEq, Debug)]
923    struct Counter(u8);
924
925    #[test]
926    fn change_detection() {
927        #[derive(Resource, Default)]
928        struct ChangeDetector;
929
930        fn count_up_iff_changed(
931            mut counter: ResMut<Counter>,
932            change_detector: ResMut<ChangeDetector>,
933        ) {
934            if change_detector.is_changed() {
935                counter.0 += 1;
936            }
937        }
938
939        let mut world = World::new();
940        world.init_resource::<ChangeDetector>();
941        world.init_resource::<Counter>();
942        assert_eq!(*world.resource::<Counter>(), Counter(0));
943        // Resources are changed when they are first added.
944        let id = world.register_system(count_up_iff_changed);
945        world.run_system(id).expect("system runs successfully");
946        assert_eq!(*world.resource::<Counter>(), Counter(1));
947        // Nothing changed
948        world.run_system(id).expect("system runs successfully");
949        assert_eq!(*world.resource::<Counter>(), Counter(1));
950        // Making a change
951        world.resource_mut::<ChangeDetector>().set_changed();
952        world.run_system(id).expect("system runs successfully");
953        assert_eq!(*world.resource::<Counter>(), Counter(2));
954    }
955
956    #[test]
957    fn local_variables() {
958        // The `Local` begins at the default value of 0
959        fn doubling(last_counter: Local<Counter>, mut counter: ResMut<Counter>) {
960            counter.0 += last_counter.0 .0;
961            last_counter.0 .0 = counter.0;
962        }
963
964        let mut world = World::new();
965        world.insert_resource(Counter(1));
966        assert_eq!(*world.resource::<Counter>(), Counter(1));
967        let id = world.register_system(doubling);
968        world.run_system(id).expect("system runs successfully");
969        assert_eq!(*world.resource::<Counter>(), Counter(1));
970        world.run_system(id).expect("system runs successfully");
971        assert_eq!(*world.resource::<Counter>(), Counter(2));
972        world.run_system(id).expect("system runs successfully");
973        assert_eq!(*world.resource::<Counter>(), Counter(4));
974        world.run_system(id).expect("system runs successfully");
975        assert_eq!(*world.resource::<Counter>(), Counter(8));
976    }
977
978    #[test]
979    fn input_values() {
980        // Verify that a non-Copy, non-Clone type can be passed in.
981        struct NonCopy(u8);
982
983        fn increment_sys(In(NonCopy(increment_by)): In<NonCopy>, mut counter: ResMut<Counter>) {
984            counter.0 += increment_by;
985        }
986
987        let mut world = World::new();
988
989        let id = world.register_system(increment_sys);
990
991        // Insert the resource after registering the system.
992        world.insert_resource(Counter(1));
993        assert_eq!(*world.resource::<Counter>(), Counter(1));
994
995        world
996            .run_system_with(id, NonCopy(1))
997            .expect("system runs successfully");
998        assert_eq!(*world.resource::<Counter>(), Counter(2));
999
1000        world
1001            .run_system_with(id, NonCopy(1))
1002            .expect("system runs successfully");
1003        assert_eq!(*world.resource::<Counter>(), Counter(3));
1004
1005        world
1006            .run_system_with(id, NonCopy(20))
1007            .expect("system runs successfully");
1008        assert_eq!(*world.resource::<Counter>(), Counter(23));
1009
1010        world
1011            .run_system_with(id, NonCopy(1))
1012            .expect("system runs successfully");
1013        assert_eq!(*world.resource::<Counter>(), Counter(24));
1014    }
1015
1016    #[test]
1017    fn output_values() {
1018        // Verify that a non-Copy, non-Clone type can be returned.
1019        #[derive(Eq, PartialEq, Debug)]
1020        struct NonCopy(u8);
1021
1022        fn increment_sys(mut counter: ResMut<Counter>) -> NonCopy {
1023            counter.0 += 1;
1024            NonCopy(counter.0)
1025        }
1026
1027        let mut world = World::new();
1028
1029        let id = world.register_system(increment_sys);
1030
1031        // Insert the resource after registering the system.
1032        world.insert_resource(Counter(1));
1033        assert_eq!(*world.resource::<Counter>(), Counter(1));
1034
1035        let output = world.run_system(id).expect("system runs successfully");
1036        assert_eq!(*world.resource::<Counter>(), Counter(2));
1037        assert_eq!(output, NonCopy(2));
1038
1039        let output = world.run_system(id).expect("system runs successfully");
1040        assert_eq!(*world.resource::<Counter>(), Counter(3));
1041        assert_eq!(output, NonCopy(3));
1042    }
1043
1044    #[test]
1045    fn fallible_system() {
1046        fn sys() -> Result<()> {
1047            Err("error")?;
1048            Ok(())
1049        }
1050
1051        let mut world = World::new();
1052        let fallible_system_id = world.register_system(sys);
1053        let output = world.run_system(fallible_system_id);
1054        assert!(matches!(output, Ok(Err(_))));
1055    }
1056
1057    #[test]
1058    fn exclusive_system() {
1059        let mut world = World::new();
1060        let exclusive_system_id = world.register_system(|world: &mut World| {
1061            world.spawn_empty();
1062        });
1063        let entity_count = world.entities.count_spawned();
1064        let _ = world.run_system(exclusive_system_id);
1065        assert_eq!(world.entities.count_spawned(), entity_count + 1);
1066    }
1067
1068    #[test]
1069    fn nested_systems() {
1070        use crate::system::SystemId;
1071
1072        #[derive(Component)]
1073        struct Callback(SystemId);
1074
1075        fn nested(query: Query<&Callback>, mut commands: Commands) {
1076            for callback in query.iter() {
1077                commands.run_system(callback.0);
1078            }
1079        }
1080
1081        let mut world = World::new();
1082        world.insert_resource(Counter(0));
1083
1084        let increment_two = world.register_system(|mut counter: ResMut<Counter>| {
1085            counter.0 += 2;
1086        });
1087        let increment_three = world.register_system(|mut counter: ResMut<Counter>| {
1088            counter.0 += 3;
1089        });
1090        let nested_id = world.register_system(nested);
1091
1092        world.spawn(Callback(increment_two));
1093        world.spawn(Callback(increment_three));
1094        let _ = world.run_system(nested_id);
1095        assert_eq!(*world.resource::<Counter>(), Counter(5));
1096    }
1097
1098    #[test]
1099    fn nested_systems_with_inputs() {
1100        use crate::system::SystemId;
1101
1102        #[derive(Component)]
1103        struct Callback(SystemId<In<u8>>, u8);
1104
1105        fn nested(query: Query<&Callback>, mut commands: Commands) {
1106            for callback in query.iter() {
1107                commands.run_system_with(callback.0, callback.1);
1108            }
1109        }
1110
1111        let mut world = World::new();
1112        world.insert_resource(Counter(0));
1113
1114        let increment_by =
1115            world.register_system(|In(amt): In<u8>, mut counter: ResMut<Counter>| {
1116                counter.0 += amt;
1117            });
1118        let nested_id = world.register_system(nested);
1119
1120        world.spawn(Callback(increment_by, 2));
1121        world.spawn(Callback(increment_by, 3));
1122        let _ = world.run_system(nested_id);
1123        assert_eq!(*world.resource::<Counter>(), Counter(5));
1124    }
1125
1126    #[test]
1127    fn cached_system() {
1128        use crate::system::RegisteredSystemError;
1129
1130        fn four() -> i32 {
1131            4
1132        }
1133
1134        let mut world = World::new();
1135        let old = world.register_system_cached(four);
1136        let new = world.register_system_cached(four);
1137        assert_eq!(old, new);
1138
1139        let result = world.unregister_system_cached(four);
1140        assert!(result.is_ok());
1141        let new = world.register_system_cached(four);
1142        assert_ne!(old, new);
1143
1144        let output = world.run_system(old);
1145        assert!(matches!(
1146            output,
1147            Err(RegisteredSystemError::SystemIdNotRegistered(x)) if x == old,
1148        ));
1149        let output = world.run_system(new);
1150        assert!(matches!(output, Ok(x) if x == four()));
1151        let output = world.run_system_cached(four);
1152        assert!(matches!(output, Ok(x) if x == four()));
1153        let output = world.run_system_cached_with(four, ());
1154        assert!(matches!(output, Ok(x) if x == four()));
1155    }
1156
1157    #[test]
1158    fn cached_fallible_system() {
1159        fn sys() -> Result<()> {
1160            Err("error")?;
1161            Ok(())
1162        }
1163
1164        let mut world = World::new();
1165        let fallible_system_id = world.register_system_cached(sys);
1166        let output = world.run_system(fallible_system_id);
1167        assert!(matches!(output, Ok(Err(_))));
1168        let output = world.run_system_cached(sys);
1169        assert!(matches!(output, Ok(Err(_))));
1170        let output = world.run_system_cached_with(sys, ());
1171        assert!(matches!(output, Ok(Err(_))));
1172    }
1173
1174    #[test]
1175    fn cached_system_commands() {
1176        fn sys(mut counter: ResMut<Counter>) {
1177            counter.0 += 1;
1178        }
1179
1180        let mut world = World::new();
1181        world.insert_resource(Counter(0));
1182        world.commands().run_system_cached(sys);
1183        world.flush_commands();
1184        assert_eq!(world.resource::<Counter>().0, 1);
1185        world.commands().run_system_cached_with(sys, ());
1186        world.flush_commands();
1187        assert_eq!(world.resource::<Counter>().0, 2);
1188    }
1189
1190    #[test]
1191    fn cached_fallible_system_commands() {
1192        fn sys(mut counter: ResMut<Counter>) -> Result {
1193            counter.0 += 1;
1194            Ok(())
1195        }
1196
1197        let mut world = World::new();
1198        world.insert_resource(Counter(0));
1199        world.commands().run_system_cached(sys);
1200        world.flush_commands();
1201        assert_eq!(world.resource::<Counter>().0, 1);
1202        world.commands().run_system_cached_with(sys, ());
1203        world.flush_commands();
1204        assert_eq!(world.resource::<Counter>().0, 2);
1205    }
1206
1207    #[test]
1208    #[should_panic(expected = "This system always fails")]
1209    fn cached_fallible_system_commands_can_fail() {
1210        use crate::system::command;
1211        fn sys() -> Result {
1212            Err("This system always fails".into())
1213        }
1214
1215        let mut world = World::new();
1216        world.commands().queue(command::run_system_cached(sys));
1217        world.flush_commands();
1218    }
1219
1220    #[test]
1221    fn cached_system_adapters() {
1222        fn four() -> i32 {
1223            4
1224        }
1225
1226        fn double(In(i): In<i32>) -> i32 {
1227            i * 2
1228        }
1229
1230        let mut world = World::new();
1231
1232        let output = world.run_system_cached(four.pipe(double));
1233        assert!(matches!(output, Ok(8)));
1234
1235        let output = world.run_system_cached(four.map(|i| i * 2));
1236        assert!(matches!(output, Ok(8)));
1237    }
1238
1239    #[test]
1240    fn cached_system_into_same_system_type() {
1241        struct Foo;
1242        impl IntoSystem<(), (), ()> for Foo {
1243            type System = ApplyDeferred;
1244            fn into_system(_: Self) -> Self::System {
1245                ApplyDeferred
1246            }
1247        }
1248
1249        struct Bar;
1250        impl IntoSystem<(), (), ()> for Bar {
1251            type System = ApplyDeferred;
1252            fn into_system(_: Self) -> Self::System {
1253                ApplyDeferred
1254            }
1255        }
1256
1257        let mut world = World::new();
1258        let foo1 = world.register_system_cached(Foo);
1259        let foo2 = world.register_system_cached(Foo);
1260        let bar1 = world.register_system_cached(Bar);
1261        let bar2 = world.register_system_cached(Bar);
1262
1263        // The `S: IntoSystem` types are different, so they should be cached
1264        // as separate systems, even though the `<S as IntoSystem>::System`
1265        // types / values are the same (`ApplyDeferred`).
1266        assert_ne!(foo1, bar1);
1267
1268        // But if the `S: IntoSystem` types are the same, they'll be cached
1269        // as the same system.
1270        assert_eq!(foo1, foo2);
1271        assert_eq!(bar1, bar2);
1272    }
1273
1274    #[test]
1275    fn system_with_input_ref() {
1276        fn with_ref(InRef(input): InRef<u8>, mut counter: ResMut<Counter>) {
1277            counter.0 += *input;
1278        }
1279
1280        let mut world = World::new();
1281        world.insert_resource(Counter(0));
1282
1283        let id = world.register_system(with_ref);
1284        world.run_system_with(id, &2).unwrap();
1285        assert_eq!(*world.resource::<Counter>(), Counter(2));
1286    }
1287
1288    #[test]
1289    fn system_with_input_mut() {
1290        #[derive(Event)]
1291        struct MyEvent {
1292            cancelled: bool,
1293        }
1294
1295        fn post(InMut(event): InMut<MyEvent>, counter: ResMut<Counter>) {
1296            if counter.0 > 0 {
1297                event.cancelled = true;
1298            }
1299        }
1300
1301        let mut world = World::new();
1302        world.insert_resource(Counter(0));
1303        let post_system = world.register_system(post);
1304
1305        let mut event = MyEvent { cancelled: false };
1306        world.run_system_with(post_system, &mut event).unwrap();
1307        assert!(!event.cancelled);
1308
1309        world.resource_mut::<Counter>().0 = 1;
1310        world.run_system_with(post_system, &mut event).unwrap();
1311        assert!(event.cancelled);
1312    }
1313
1314    #[test]
1315    fn run_system_invalid_params() {
1316        use crate::system::RegisteredSystemError;
1317        use alloc::string::ToString;
1318
1319        #[derive(Resource)]
1320        struct T;
1321
1322        fn system(_: Res<T>) {}
1323
1324        let mut world = World::new();
1325        let id = world.register_system(system);
1326        // This fails because `T` has not been added to the world yet.
1327        let result = world.run_system(id);
1328
1329        assert!(matches!(result, Err(RegisteredSystemError::Failed { .. })));
1330        let expected = "does not exist";
1331        let actual = result.unwrap_err().to_string();
1332
1333        assert!(
1334            actual.contains(expected),
1335            "Expected error message to contain `{}` but got `{}`",
1336            expected,
1337            actual
1338        );
1339    }
1340
1341    #[test]
1342    fn run_system_recursive() {
1343        std::thread_local! {
1344            static INVOCATIONS_LEFT: Cell<i32> = const { Cell::new(3) };
1345            static SYSTEM_ID: Cell<Option<SystemId>> = default();
1346        }
1347
1348        fn system(mut commands: Commands) {
1349            let count = INVOCATIONS_LEFT.get() - 1;
1350            INVOCATIONS_LEFT.set(count);
1351            if count > 0 {
1352                commands.run_system(SYSTEM_ID.get().unwrap());
1353            }
1354        }
1355
1356        let mut world = World::new();
1357        let id = world.register_system(system);
1358        SYSTEM_ID.set(Some(id));
1359        world.run_system(id).unwrap();
1360
1361        assert_eq!(INVOCATIONS_LEFT.get(), 0);
1362    }
1363
1364    #[test]
1365    fn run_system_exclusive_adapters() {
1366        let mut world = World::new();
1367        fn system(_: &mut World) {}
1368        world.run_system_cached(system).unwrap();
1369        world.run_system_cached(system.pipe(system)).unwrap();
1370        world.run_system_cached(system.map(|()| {})).unwrap();
1371    }
1372
1373    #[test]
1374    fn wrong_system_type() {
1375        fn test() -> Result<(), u8> {
1376            Ok(())
1377        }
1378
1379        let mut world = World::new();
1380
1381        let entity = world.register_system_cached(test).entity();
1382
1383        match world.run_system::<u8>(SystemId::from_entity(entity)) {
1384            Ok(_) => panic!("Should fail since called `run_system` with wrong SystemId type."),
1385            Err(RegisteredSystemError::IncorrectType(_, _)) => (),
1386            Err(err) => panic!("Failed with wrong error. `{:?}`", err),
1387        }
1388    }
1389
1390    #[test]
1391    fn despawn_unused() {
1392        let mut world = World::new();
1393
1394        fn system() {}
1395
1396        let handle = world.register_tracked_system(system);
1397        let entity = handle.entity();
1398        drop(handle);
1399
1400        assert!(world.get_entity(entity).is_ok());
1401
1402        world
1403            .run_system_cached(despawn_unused_registered_systems)
1404            .unwrap();
1405
1406        assert!(world.get_entity(entity).is_err());
1407    }
1408
1409    #[test]
1410    fn system_handle_template() {
1411        fn my_system() {}
1412
1413        let mut world = World::new();
1414
1415        {
1416            let my_system_handle = world.register_tracked_system(my_system);
1417            let system_handle = world
1418                .spawn_empty()
1419                .build_template(&SystemHandleTemplate::Handle(my_system_handle.clone()))
1420                .unwrap();
1421            assert_eq!(system_handle, my_system_handle);
1422        }
1423
1424        {
1425            let template = system_value(my_system);
1426
1427            let a = world.spawn_empty().build_template(&template).unwrap();
1428            let b = world.spawn_empty().build_template(&template).unwrap();
1429
1430            assert!(matches!(a, SystemHandle::Strong(_)));
1431            assert!(matches!(b, SystemHandle::Strong(_)));
1432
1433            assert_eq!(a, b);
1434        }
1435    }
1436
1437    #[test]
1438    fn run_system_with_owned_system_handle() {
1439        fn increment(mut counter: ResMut<Counter>) {
1440            counter.0 += 1;
1441        }
1442
1443        let mut world = World::new();
1444        world.insert_resource(Counter(0));
1445
1446        let handle = world.register_tracked_system(increment);
1447        world.run_system(handle).expect("system runs successfully");
1448
1449        assert_eq!(*world.resource::<Counter>(), Counter(1));
1450    }
1451}