Skip to main content

bevy_ecs/system/
system_name.rs

1use crate::{
2    change_detection::Tick,
3    prelude::World,
4    query::FilteredAccessSet,
5    system::{
6        ExclusiveSystemParam, ReadOnlySystemParam, SystemMeta, SystemParam,
7        SystemParamValidationError,
8    },
9    world::unsafe_world_cell::UnsafeWorldCell,
10};
11use bevy_utils::prelude::DebugName;
12use derive_more::derive::{Display, Into};
13
14/// [`SystemParam`] that returns the name of the system which it is used in.
15///
16/// This is not a reliable identifier, it is more so useful for debugging or logging.
17///
18/// # Examples
19///
20/// ```
21/// # use bevy_ecs::system::SystemName;
22/// # use bevy_ecs::system::SystemParam;
23///
24/// #[derive(SystemParam)]
25/// struct Logger {
26///     system_name: SystemName,
27/// }
28///
29/// impl Logger {
30///     fn log(&mut self, message: &str) {
31///         eprintln!("{}: {}", self.system_name, message);
32///     }
33/// }
34///
35/// fn system1(mut logger: Logger) {
36///     // Prints: "crate_name::mod_name::system1: Hello".
37///     logger.log("Hello");
38/// }
39/// ```
40#[derive(Debug, Into, Display)]
41pub struct SystemName(DebugName);
42
43impl SystemName {
44    /// Gets the name of the system.
45    pub fn name(&self) -> DebugName {
46        self.0.clone()
47    }
48}
49
50// SAFETY: no component value access
51unsafe impl SystemParam for SystemName {
52    type State = ();
53    type Item<'w, 's> = SystemName;
54
55    fn init_state(_world: &mut World) -> Self::State {}
56
57    fn init_access(
58        _state: &Self::State,
59        _system_meta: &mut SystemMeta,
60        _component_access_set: &mut FilteredAccessSet,
61        _world: &mut World,
62    ) {
63    }
64
65    #[inline]
66    unsafe fn get_param<'w, 's>(
67        _state: &'s mut Self::State,
68        system_meta: &SystemMeta,
69        _world: UnsafeWorldCell<'w>,
70        _change_tick: Tick,
71    ) -> Result<Self::Item<'w, 's>, SystemParamValidationError> {
72        Ok(SystemName(system_meta.name.clone()))
73    }
74}
75
76// SAFETY: Only reads internal system state
77unsafe impl ReadOnlySystemParam for SystemName {}
78
79impl ExclusiveSystemParam for SystemName {
80    type State = ();
81    type Item<'s> = SystemName;
82
83    fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {}
84
85    fn get_param<'s>(
86        _state: &'s mut Self::State,
87        system_meta: &SystemMeta,
88    ) -> Result<Self::Item<'s>, SystemParamValidationError> {
89        Ok(SystemName(system_meta.name.clone()))
90    }
91}
92
93#[cfg(test)]
94#[cfg(all(feature = "trace", feature = "debug"))]
95mod tests {
96    use crate::{
97        system::{IntoSystem, RunSystemOnce, SystemName},
98        world::World,
99    };
100    use alloc::{borrow::ToOwned, string::String};
101
102    #[test]
103    fn test_system_name_regular_param() {
104        fn testing(name: SystemName) -> String {
105            name.name().as_string()
106        }
107
108        let mut world = World::default();
109        let id = world.register_system(testing);
110        let name = world.run_system(id).unwrap();
111        assert!(name.ends_with("testing"));
112    }
113
114    #[test]
115    fn test_system_name_exclusive_param() {
116        fn testing(_world: &mut World, name: SystemName) -> String {
117            name.name().as_string()
118        }
119
120        let mut world = World::default();
121        let id = world.register_system(testing);
122        let name = world.run_system(id).unwrap();
123        assert!(name.ends_with("testing"));
124    }
125
126    #[test]
127    fn test_closure_system_name_regular_param() {
128        let mut world = World::default();
129        let system =
130            IntoSystem::into_system(|name: SystemName| name.name().to_owned()).with_name("testing");
131        let name = world.run_system_once(system).unwrap().as_string();
132        assert_eq!(name, "testing");
133    }
134
135    #[test]
136    fn test_exclusive_closure_system_name_regular_param() {
137        let mut world = World::default();
138        let system =
139            IntoSystem::into_system(|_world: &mut World, name: SystemName| name.name().to_owned())
140                .with_name("testing");
141        let name = world.run_system_once(system).unwrap().as_string();
142        assert_eq!(name, "testing");
143    }
144}