1use std::sync::Arc;
4
5use crate::prelude::*;
6
7pub trait System<In, Out> {
9 fn run(&mut self, world: &World, input: In) -> Out;
11 fn name(&self) -> &str;
13}
14
15pub struct StaticSystem<In, Out> {
17 pub run: Box<dyn FnMut(&World, In) -> Out + Send + Sync>,
19 pub name: &'static str,
21}
22
23impl<In, Out> System<In, Out> for StaticSystem<In, Out> {
24 fn run(&mut self, world: &World, input: In) -> Out {
25 (self.run)(world, input)
26 }
27 fn name(&self) -> &str {
28 self.name
29 }
30}
31
32pub trait IntoSystem<Args, In, Out> {
45 type Sys: System<In, Out>;
47
48 fn system(self) -> Self::Sys;
50}
51
52impl<T, In, Out> IntoSystem<T, In, Out> for T
53where
54 T: System<In, Out>,
55{
56 type Sys = T;
57 fn system(self) -> Self::Sys {
58 self
59 }
60}
61
62pub trait SystemParam: Sized {
71 type State;
73 type Param<'s>;
81 fn get_state(world: &World) -> Self::State;
86 #[allow(clippy::needless_lifetimes)] fn borrow<'s>(world: &'s World, state: &'s mut Self::State) -> Self::Param<'s>;
90}
91
92impl SystemParam for &'_ World {
93 type State = ();
94 type Param<'s> = &'s World;
95 fn get_state(_world: &World) -> Self::State {}
96 fn borrow<'s>(world: &'s World, _state: &'s mut Self::State) -> Self::Param<'s> {
97 world
98 }
99}
100
101#[derive(Deref, DerefMut)]
103pub struct In<T>(pub T);
104
105pub struct Res<'a, T: HasSchema>(Ref<'a, T>);
109impl<'a, T: HasSchema> std::ops::Deref for Res<'a, T> {
110 type Target = T;
111 fn deref(&self) -> &Self::Target {
112 &self.0
113 }
114}
115
116pub struct ResInit<'a, T: HasSchema + FromWorld>(Ref<'a, T>);
121impl<'a, T: HasSchema + FromWorld> std::ops::Deref for ResInit<'a, T> {
122 type Target = T;
123 fn deref(&self) -> &Self::Target {
124 &self.0
125 }
126}
127
128pub struct ResMut<'a, T: HasSchema>(RefMut<'a, T>);
132impl<'a, T: HasSchema> std::ops::Deref for ResMut<'a, T> {
133 type Target = T;
134 fn deref(&self) -> &Self::Target {
135 &self.0
136 }
137}
138impl<'a, T: HasSchema> std::ops::DerefMut for ResMut<'a, T> {
139 fn deref_mut(&mut self) -> &mut Self::Target {
140 &mut self.0
141 }
142}
143
144pub struct ResMutInit<'a, T: HasSchema + FromWorld>(RefMut<'a, T>);
149impl<'a, T: HasSchema + FromWorld> std::ops::Deref for ResMutInit<'a, T> {
150 type Target = T;
151 fn deref(&self) -> &Self::Target {
152 &self.0
153 }
154}
155impl<'a, T: HasSchema + FromWorld> std::ops::DerefMut for ResMutInit<'a, T> {
156 fn deref_mut(&mut self) -> &mut Self::Target {
157 &mut self.0
158 }
159}
160
161impl<'a, T: HasSchema> SystemParam for Res<'a, T> {
162 type State = AtomicResource<T>;
163 type Param<'p> = Res<'p, T>;
164
165 fn get_state(world: &World) -> Self::State {
166 world.resources.get_cell::<T>()
167 }
168
169 fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
170 Res(state.borrow().unwrap_or_else(|| {
171 panic!(
172 "Resource of type `{}` not in world. \
173 You may need to insert or initialize the resource or use \
174 `ResInit` instead of `Res` to automatically initialize the \
175 resource with the default value.",
176 std::any::type_name::<T>()
177 )
178 }))
179 }
180}
181
182impl<'a, T: HasSchema> SystemParam for Option<Res<'a, T>> {
183 type State = AtomicResource<T>;
184 type Param<'p> = Option<Res<'p, T>>;
185
186 fn get_state(world: &World) -> Self::State {
187 world.resources.get_cell::<T>()
188 }
189
190 fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
191 state.borrow().map(|x| Res(x))
192 }
193}
194
195impl<'a, T: HasSchema + FromWorld> SystemParam for ResInit<'a, T> {
196 type State = AtomicResource<T>;
197 type Param<'p> = ResInit<'p, T>;
198
199 fn get_state(world: &World) -> Self::State {
200 let cell = world.resources.get_cell::<T>();
201 cell.init(world);
202 cell
203 }
204
205 fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
206 ResInit(state.borrow().unwrap())
207 }
208}
209
210impl<'a, T: HasSchema> SystemParam for ResMut<'a, T> {
211 type State = AtomicResource<T>;
212 type Param<'p> = ResMut<'p, T>;
213
214 fn get_state(world: &World) -> Self::State {
215 world.resources.get_cell::<T>()
216 }
217
218 fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
219 ResMut(state.borrow_mut().unwrap_or_else(|| {
220 panic!(
221 "Resource of type `{}` not in world. \
222 You may need to insert or initialize the resource or use \
223 `ResMutInit` instead of `ResMut` to automatically initialize the \
224 resource with the default value.",
225 std::any::type_name::<T>()
226 )
227 }))
228 }
229}
230
231impl<'a, T: HasSchema> SystemParam for Option<ResMut<'a, T>> {
232 type State = AtomicResource<T>;
233 type Param<'p> = Option<ResMut<'p, T>>;
234
235 fn get_state(world: &World) -> Self::State {
236 world.resources.get_cell::<T>()
237 }
238
239 fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
240 state.borrow_mut().map(|state| ResMut(state))
241 }
242}
243
244impl<'a, T: HasSchema + FromWorld> SystemParam for ResMutInit<'a, T> {
245 type State = AtomicResource<T>;
246 type Param<'p> = ResMutInit<'p, T>;
247
248 fn get_state(world: &World) -> Self::State {
249 let cell = world.resources.get_cell::<T>();
250 cell.init(world);
251 cell
252 }
253
254 fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
255 ResMutInit(state.borrow_mut().unwrap())
256 }
257}
258
259pub type Comp<'a, T> = Ref<'a, ComponentStore<T>>;
261pub type CompMut<'a, T> = RefMut<'a, ComponentStore<T>>;
263
264impl<'a, T: HasSchema> SystemParam for Comp<'a, T> {
265 type State = Arc<AtomicCell<ComponentStore<T>>>;
266 type Param<'p> = Comp<'p, T>;
267
268 fn get_state(world: &World) -> Self::State {
269 world.components.get_cell::<T>()
270 }
271
272 fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
273 state.borrow()
274 }
275}
276
277impl<'a, T: HasSchema> SystemParam for CompMut<'a, T> {
278 type State = Arc<AtomicCell<ComponentStore<T>>>;
279 type Param<'p> = CompMut<'p, T>;
280
281 fn get_state(world: &World) -> Self::State {
282 world.components.get_cell::<T>()
283 }
284
285 fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
286 state.borrow_mut()
287 }
288}
289
290macro_rules! impl_system {
291 ($($args:ident,)*) => {
292 #[allow(unused_parens)]
293 impl<
294 F,
295 Out,
296 $(
297 $args: SystemParam,
298 )*
299 > IntoSystem<(F, $($args,)*), (), Out> for F
300 where for<'a> F: 'static + Send + Sync +
301 FnMut(
302 $(
303 <$args as SystemParam>::Param<'a>,
304 )*
305 ) -> Out +
306 FnMut(
307 $(
308 $args,
309 )*
310 ) -> Out
311 {
312 type Sys = StaticSystem<(), Out>;
313 fn system(mut self) -> Self::Sys {
314 StaticSystem {
315 name: std::any::type_name::<F>(),
316 run: Box::new(move |_world, _input| {
317 $(
318 #[allow(non_snake_case)]
319 let mut $args = $args::get_state(_world);
320 )*
321
322 self(
323 $(
324 $args::borrow(_world, &mut $args),
325 )*
326 )
327 })
328 }
329 }
330 }
331 };
332}
333
334macro_rules! impl_system_with_input {
335 ($($args:ident,)*) => {
336 #[allow(unused_parens)]
337 impl<
338 'input,
339 F,
340 InT: 'input,
341 Out,
342 $(
343 $args: SystemParam,
344 )*
345 > IntoSystem<(F, InT, $($args,)*), InT, Out> for F
346 where for<'a> F: 'static + Send + Sync +
347 FnMut(
348 In<InT>,
349 $(
350 <$args as SystemParam>::Param<'a>,
351 )*
352 ) -> Out +
353 FnMut(
354 In<InT>,
355 $(
356 $args,
357 )*
358 ) -> Out
359 {
360 type Sys = StaticSystem<InT, Out>;
361 fn system(mut self) -> Self::Sys {
362 StaticSystem {
363 name: std::any::type_name::<F>(),
364 run: Box::new(move |_world, input| {
365 $(
366 #[allow(non_snake_case)]
367 let mut $args = $args::get_state(_world);
368 )*
369
370 self(
371 In(input),
372 $(
373 $args::borrow(_world, &mut $args),
374 )*
375 )
376 })
377 }
378 }
379 }
380 };
381}
382
383macro_rules! impl_systems {
384 () => {
386 impl_system!();
387 impl_system_with_input!();
388 };
389 ($head:ident, $($idents:ident,)*) => {
391 impl_system!($head, $($idents,)*);
392 impl_system_with_input!($head, $($idents,)*);
393 impl_systems!($($idents,)*);
394 }
395}
396
397impl_systems!(A, B, C, D, E, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,);
398
399#[cfg(test)]
400mod tests {
401 use crate::prelude::*;
402
403 #[test]
404 fn convert_system() {
405 fn tmp(
406 _var1: Ref<ComponentStore<u32>>,
407 _var2: Ref<ComponentStore<u64>>,
408 _var3: Res<i32>,
409 _var4: ResMut<i64>,
410 ) -> u32 {
411 0
412 }
413 #[allow(clippy::too_many_arguments)]
416 fn tmp2(
417 _var7: Comp<i64>,
418 _var8: CompMut<i64>,
419 _var1: Res<u32>,
420 _var2: ResMut<u64>,
421 _var3: Res<u32>,
422 _var4: ResMut<u64>,
423 _var5: Res<u32>,
424 _var6: ResMut<u64>,
425 _var9: Comp<i64>,
426 _var10: CompMut<i64>,
427 _var11: Comp<i64>,
428 _var12: CompMut<u64>,
429 ) {
430 }
431 fn tmp3(_in: In<usize>, _comp1: Comp<i64>) {}
432 let _ = tmp.system();
433 let _ = tmp2.system();
434 let _ = tmp3.system();
435 }
436
437 #[test]
438 fn system_is_send() {
439 let x = 6;
440 send(
441 (move |_var1: Res<u32>| {
442 let _y = x;
443 })
444 .system(),
445 );
446 send((|| ()).system());
447 send(sys.system());
448 }
449
450 fn sys(_var1: Res<u32>) {}
451 fn send<T: Send>(_t: T) {}
452
453 #[test]
454 fn optional_resource() {
455 fn access_resource(
456 a: Option<Res<u8>>,
457 b: Option<Res<u16>>,
458 c: Option<ResMut<u32>>,
459 d: Option<ResMut<u64>>,
460 ) {
461 assert!(a.as_deref().is_none());
462 assert!(b.as_deref() == Some(&1));
463 assert!(c.as_deref().is_none());
464 assert!(d.as_deref() == Some(&2));
465 }
466
467 let mut world = World::new();
468 world.insert_resource(1u16);
469 world.insert_resource(2u64);
470 world.run_system(access_resource, ());
471 }
472
473 #[test]
474 fn in_and_out() {
475 fn mul_by_res(n: In<usize>, r: Res<usize>) -> usize {
476 *n * *r
477 }
478
479 fn sys_with_ref_in(mut n: In<&mut usize>) {
480 **n *= 3;
481 }
482
483 let mut world = World::new();
484 world.insert_resource(2usize);
485
486 let result = world.run_system(mul_by_res, 3);
487 assert_eq!(result, 6);
488
489 let mut n = 3;
490 world.run_system(sys_with_ref_in, &mut n)
491 }
492
493 #[test]
494 fn system_replace_resource() {
495 #[derive(Default, HasSchema, Clone, PartialEq, Eq, Debug)]
496 pub struct A;
497 #[derive(Default, HasSchema, Clone, Debug)]
498 pub struct B {
499 x: u32,
500 }
501 let world = World::default();
502 let my_system = (|_a: ResInit<A>, mut b: ResMutInit<B>| {
503 let b2 = B { x: 45 };
504 *b = b2;
505 })
506 .system();
507
508 assert!(world.resources.get_cell::<B>().borrow().is_none());
509 world.run_system(my_system, ());
510
511 let res = world.resource::<B>();
512 assert_eq!(res.x, 45);
513
514 let res = world.resource::<A>();
515 assert_eq!(*res, A);
516 }
517}