1use crate::commands::CommandQueue;
2use crate::scheduler::{ScheduleStage, SystemConfig};
3use crate::simple_world::SimpleWorld;
4
5pub struct SystemContext<'w, 'e, 'c> {
7 world: &'w mut SimpleWorld,
8 env: &'e soroban_sdk::Env,
9 commands: &'c mut CommandQueue,
10}
11
12impl<'w, 'e, 'c> SystemContext<'w, 'e, 'c> {
13 pub fn new(
14 world: &'w mut SimpleWorld,
15 env: &'e soroban_sdk::Env,
16 commands: &'c mut CommandQueue,
17 ) -> Self {
18 Self {
19 world,
20 env,
21 commands,
22 }
23 }
24
25 pub fn world(&self) -> &SimpleWorld {
26 self.world
27 }
28
29 pub fn world_mut(&mut self) -> &mut SimpleWorld {
30 self.world
31 }
32
33 pub fn env(&self) -> &soroban_sdk::Env {
34 self.env
35 }
36
37 pub fn commands(&mut self) -> &mut CommandQueue {
38 self.commands
39 }
40}
41
42pub trait SimpleSystem {
44 fn run(&mut self, context: &mut SystemContext<'_, '_, '_>);
45}
46
47pub trait AppSystem: SimpleSystem {}
51
52impl<T: SimpleSystem + ?Sized> AppSystem for T {}
53
54pub struct SystemSpec<S> {
60 name: &'static str,
61 system: S,
62 config: SystemConfig,
63}
64
65impl<S> SystemSpec<S> {
66 pub fn new(name: &'static str, system: S) -> Self {
67 Self {
68 name,
69 system,
70 config: SystemConfig::default(),
71 }
72 }
73
74 pub fn in_stage(mut self, stage: ScheduleStage) -> Self {
75 self.config = self.config.in_stage(stage);
76 self
77 }
78
79 pub fn after(mut self, system_name: impl Into<alloc::string::String>) -> Self {
80 self.config = self.config.after(system_name);
81 self
82 }
83
84 pub fn before(mut self, system_name: impl Into<alloc::string::String>) -> Self {
85 self.config = self.config.before(system_name);
86 self
87 }
88
89 pub fn in_set(mut self, set_name: impl Into<alloc::string::String>) -> Self {
90 self.config = self.config.in_set(set_name);
91 self
92 }
93
94 pub fn after_set(mut self, set_name: impl Into<alloc::string::String>) -> Self {
95 self.config = self.config.after_set(set_name);
96 self
97 }
98
99 pub fn before_set(mut self, set_name: impl Into<alloc::string::String>) -> Self {
100 self.config = self.config.before_set(set_name);
101 self
102 }
103
104 pub fn with_config(mut self, config: SystemConfig) -> Self {
105 self.config = config;
106 self
107 }
108
109 pub fn name(&self) -> &'static str {
110 self.name
111 }
112
113 pub fn config(&self) -> &SystemConfig {
114 &self.config
115 }
116
117 pub(crate) fn into_parts(self) -> (&'static str, SystemConfig, S) {
118 (self.name, self.config, self.system)
119 }
120}
121
122pub struct ContextSystem<F> {
124 function: F,
125}
126
127impl<F> ContextSystem<F> {
128 pub fn new(function: F) -> Self {
129 Self { function }
130 }
131}
132
133impl<F> SimpleSystem for ContextSystem<F>
134where
135 F: for<'w, 'e, 'c> FnMut(&mut SystemContext<'w, 'e, 'c>),
136{
137 fn run(&mut self, context: &mut SystemContext<'_, '_, '_>) {
138 (self.function)(context);
139 }
140}
141
142pub struct WorldSystem<F> {
144 function: F,
145}
146
147impl<F> WorldSystem<F> {
148 pub fn new(function: F) -> Self {
149 Self { function }
150 }
151}
152
153impl<F> SimpleSystem for WorldSystem<F>
154where
155 F: FnMut(&mut SimpleWorld, &soroban_sdk::Env),
156{
157 fn run(&mut self, context: &mut SystemContext<'_, '_, '_>) {
158 let env = context.env().clone();
159 (self.function)(context.world_mut(), &env);
160 }
161}
162
163pub fn context_system<F>(function: F) -> ContextSystem<F>
165where
166 F: for<'w, 'e, 'c> FnMut(&mut SystemContext<'w, 'e, 'c>),
167{
168 ContextSystem::new(function)
169}
170
171pub fn world_system<F>(function: F) -> WorldSystem<F>
173where
174 F: FnMut(&mut SimpleWorld, &soroban_sdk::Env),
175{
176 WorldSystem::new(function)
177}
178
179pub fn named_system<F>(name: &'static str, function: F) -> SystemSpec<WorldSystem<F>>
181where
182 F: FnMut(&mut SimpleWorld, &soroban_sdk::Env),
183{
184 SystemSpec::new(name, world_system(function))
185}
186
187pub fn named_context_system<F>(name: &'static str, function: F) -> SystemSpec<ContextSystem<F>>
189where
190 F: for<'w, 'e, 'c> FnMut(&mut SystemContext<'w, 'e, 'c>),
191{
192 SystemSpec::new(name, context_system(function))
193}
194
195pub fn named_app_system<S>(name: &'static str, system: S) -> SystemSpec<S>
197where
198 S: AppSystem,
199{
200 SystemSpec::new(name, system)
201}
202
203#[cfg(test)]
204mod tests {
205 use super::{
206 named_context_system, named_system, AppSystem, ContextSystem, SimpleSystem, SystemContext,
207 WorldSystem,
208 };
209 use crate::commands::CommandQueue;
210 use crate::simple_world::SimpleWorld;
211 use soroban_sdk::{symbol_short, Bytes, Env};
212
213 #[test]
214 fn world_system_wraps_world_and_env_closure() {
215 let env = Env::default();
216 let mut world = SimpleWorld::new(&env);
217 let mut commands = CommandQueue::new();
218 let mut system = WorldSystem::new(|world: &mut SimpleWorld, env: &Env| {
219 let entity = world.spawn_entity();
220 world.add_component(entity, symbol_short!("tag"), Bytes::from_array(env, &[1]));
221 });
222 let mut context = SystemContext::new(&mut world, &env, &mut commands);
223
224 system.run(&mut context);
225
226 assert!(world.has_component(1, &symbol_short!("tag")));
227 }
228
229 #[test]
230 fn context_system_wraps_context_closure() {
231 let env = Env::default();
232 let mut world = SimpleWorld::new(&env);
233 let mut commands = CommandQueue::new();
234 let mut system = ContextSystem::new(|context: &mut SystemContext<'_, '_, '_>| {
235 context.commands().spawn();
236 });
237 let mut context = SystemContext::new(&mut world, &env, &mut commands);
238
239 system.run(&mut context);
240 let spawned = commands.apply(&mut world);
241
242 assert_eq!(spawned.len(), 1);
243 }
244
245 #[test]
246 fn named_helpers_preserve_registration_name() {
247 let world_spec = named_system("tick", |_world: &mut SimpleWorld, _env: &Env| {});
248 let context_spec = named_context_system("ctx", |_context| {});
249
250 assert_eq!(world_spec.name(), "tick");
251 assert_eq!(context_spec.name(), "ctx");
252 }
253
254 fn accepts_app_system<S: AppSystem>(_system: &S) {}
255
256 #[test]
257 fn runtime_adapters_implement_app_system() {
258 let world_system = WorldSystem::new(|_world: &mut SimpleWorld, _env: &Env| {});
259 let context_system = ContextSystem::new(|_context: &mut SystemContext<'_, '_, '_>| {});
260
261 accepts_app_system(&world_system);
262 accepts_app_system(&context_system);
263 }
264}