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 + FromWorld> SystemParam for ResInit<'a, T> {
183 type State = AtomicResource<T>;
184 type Param<'p> = ResInit<'p, T>;
185
186 fn get_state(world: &World) -> Self::State {
187 let cell = world.resources.get_cell::<T>();
188 cell.init(world);
189 cell
190 }
191
192 fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
193 ResInit(state.borrow().unwrap())
194 }
195}
196
197impl<'a, T: HasSchema> SystemParam for ResMut<'a, T> {
198 type State = AtomicResource<T>;
199 type Param<'p> = ResMut<'p, T>;
200
201 fn get_state(world: &World) -> Self::State {
202 world.resources.get_cell::<T>()
203 }
204
205 fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
206 ResMut(state.borrow_mut().unwrap_or_else(|| {
207 panic!(
208 "Resource of type `{}` not in world. \
209 You may need to insert or initialize the resource or use \
210 `ResMutInit` instead of `ResMut` to automatically initialize the \
211 resource with the default value.",
212 std::any::type_name::<T>()
213 )
214 }))
215 }
216}
217
218impl<'a, T: HasSchema + FromWorld> SystemParam for ResMutInit<'a, T> {
219 type State = AtomicResource<T>;
220 type Param<'p> = ResMutInit<'p, T>;
221
222 fn get_state(world: &World) -> Self::State {
223 let cell = world.resources.get_cell::<T>();
224 cell.init(world);
225 cell
226 }
227
228 fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
229 ResMutInit(state.borrow_mut().unwrap())
230 }
231}
232
233pub type Comp<'a, T> = Ref<'a, ComponentStore<T>>;
235pub type CompMut<'a, T> = RefMut<'a, ComponentStore<T>>;
237
238impl<'a, T: HasSchema> SystemParam for Comp<'a, T> {
239 type State = Arc<AtomicCell<ComponentStore<T>>>;
240 type Param<'p> = Comp<'p, T>;
241
242 fn get_state(world: &World) -> Self::State {
243 world.components.get_cell::<T>()
244 }
245
246 fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
247 state.borrow()
248 }
249}
250
251impl<'a, T: HasSchema> SystemParam for CompMut<'a, T> {
252 type State = Arc<AtomicCell<ComponentStore<T>>>;
253 type Param<'p> = CompMut<'p, T>;
254
255 fn get_state(world: &World) -> Self::State {
256 world.components.get_cell::<T>()
257 }
258
259 fn borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
260 state.borrow_mut()
261 }
262}
263
264pub trait OptionalSystemParam: SystemParam {
277 fn try_get_state(world: &World) -> Option<Self::State>;
282 #[allow(clippy::needless_lifetimes)] fn try_borrow<'s>(world: &'s World, state: &'s mut Self::State) -> Option<Self::Param<'s>>;
286}
287impl<T: OptionalSystemParam> SystemParam for Option<T> {
288 type State = Option<T::State>;
289 type Param<'s> = Option<T::Param<'s>>;
290
291 fn get_state(world: &World) -> Self::State {
292 T::try_get_state(world)
293 }
294 fn borrow<'s>(world: &'s World, state: &'s mut Self::State) -> Self::Param<'s> {
295 state.as_mut().and_then(|state| T::try_borrow(world, state))
296 }
297}
298
299impl<'a, T: HasSchema> OptionalSystemParam for Res<'a, T> {
300 fn try_get_state(world: &World) -> Option<Self::State> {
301 Some(world.resources.get_cell::<T>())
302 }
303 fn try_borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Option<Self::Param<'s>> {
304 state.borrow().map(|x| Res(x))
305 }
306}
307
308impl<'a, T: HasSchema> OptionalSystemParam for ResMut<'a, T> {
309 fn try_get_state(world: &World) -> Option<Self::State> {
310 Some(world.resources.get_cell::<T>())
311 }
312 fn try_borrow<'s>(_world: &'s World, state: &'s mut Self::State) -> Option<Self::Param<'s>> {
313 state.borrow_mut().map(|x| ResMut(x))
314 }
315}
316
317macro_rules! impl_system {
318 ($($args:ident,)*) => {
319 #[allow(unused_parens)]
320 impl<
321 F,
322 Out,
323 $(
324 $args: SystemParam,
325 )*
326 > IntoSystem<(F, $($args,)*), (), Out> for F
327 where for<'a> F: 'static + Send + Sync +
328 FnMut(
329 $(
330 <$args as SystemParam>::Param<'a>,
331 )*
332 ) -> Out +
333 FnMut(
334 $(
335 $args,
336 )*
337 ) -> Out
338 {
339 type Sys = StaticSystem<(), Out>;
340 fn system(mut self) -> Self::Sys {
341 StaticSystem {
342 name: std::any::type_name::<F>(),
343 run: Box::new(move |_world, _input| {
344 $(
345 #[allow(non_snake_case)]
346 let mut $args = $args::get_state(_world);
347 )*
348
349 self(
350 $(
351 $args::borrow(_world, &mut $args),
352 )*
353 )
354 })
355 }
356 }
357 }
358 };
359}
360
361macro_rules! impl_system_with_input {
362 ($($args:ident,)*) => {
363 #[allow(unused_parens)]
364 impl<
365 'input,
366 F,
367 InT: 'input,
368 Out,
369 $(
370 $args: SystemParam,
371 )*
372 > IntoSystem<(F, InT, $($args,)*), InT, Out> for F
373 where for<'a> F: 'static + Send + Sync +
374 FnMut(
375 In<InT>,
376 $(
377 <$args as SystemParam>::Param<'a>,
378 )*
379 ) -> Out +
380 FnMut(
381 In<InT>,
382 $(
383 $args,
384 )*
385 ) -> Out
386 {
387 type Sys = StaticSystem<InT, Out>;
388 fn system(mut self) -> Self::Sys {
389 StaticSystem {
390 name: std::any::type_name::<F>(),
391 run: Box::new(move |_world, input| {
392 $(
393 #[allow(non_snake_case)]
394 let mut $args = $args::get_state(_world);
395 )*
396
397 self(
398 In(input),
399 $(
400 $args::borrow(_world, &mut $args),
401 )*
402 )
403 })
404 }
405 }
406 }
407 };
408}
409
410macro_rules! impl_systems {
411 () => {
413 impl_system!();
414 impl_system_with_input!();
415 };
416 ($head:ident, $($idents:ident,)*) => {
418 impl_system!($head, $($idents,)*);
419 impl_system_with_input!($head, $($idents,)*);
420 impl_systems!($($idents,)*);
421 }
422}
423
424impl_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,);
425
426#[cfg(test)]
427mod tests {
428 use crate::prelude::*;
429
430 #[test]
431 fn convert_system() {
432 fn tmp(
433 _var1: Ref<ComponentStore<u32>>,
434 _var2: Ref<ComponentStore<u64>>,
435 _var3: Res<i32>,
436 _var4: ResMut<i64>,
437 ) -> u32 {
438 0
439 }
440 #[allow(clippy::too_many_arguments)]
443 fn tmp2(
444 _var7: Comp<i64>,
445 _var8: CompMut<i64>,
446 _var1: Res<u32>,
447 _var2: ResMut<u64>,
448 _var3: Res<u32>,
449 _var4: ResMut<u64>,
450 _var5: Res<u32>,
451 _var6: ResMut<u64>,
452 _var9: Comp<i64>,
453 _var10: CompMut<i64>,
454 _var11: Comp<i64>,
455 _var12: CompMut<u64>,
456 ) {
457 }
458 fn tmp3(_in: In<usize>, _comp1: Comp<i64>) {}
459 let _ = tmp.system();
460 let _ = tmp2.system();
461 let _ = tmp3.system();
462 }
463
464 #[test]
465 fn system_is_send() {
466 let x = 6;
467 send(
468 (move |_var1: Res<u32>| {
469 let _y = x;
470 })
471 .system(),
472 );
473 send((|| ()).system());
474 send(sys.system());
475 }
476
477 fn sys(_var1: Res<u32>) {}
478 fn send<T: Send>(_t: T) {}
479
480 #[test]
481 fn optional_resource() {
482 fn access_resource(
483 a: Option<Res<u8>>,
484 b: Option<Res<u16>>,
485 c: Option<ResMut<u32>>,
486 d: Option<ResMut<u64>>,
487 ) {
488 assert!(a.as_deref().is_none());
489 assert!(b.as_deref() == Some(&1));
490 assert!(c.as_deref().is_none());
491 assert!(d.as_deref() == Some(&2));
492 }
493
494 let world = World::new();
495 world.insert_resource(1u16);
496 world.insert_resource(2u64);
497 world.run_system(access_resource, ());
498 }
499
500 #[test]
501 fn in_and_out() {
502 fn mul_by_res(n: In<usize>, r: Res<usize>) -> usize {
503 *n * *r
504 }
505
506 fn sys_with_ref_in(mut n: In<&mut usize>) {
507 **n *= 3;
508 }
509
510 let world = World::new();
511 world.insert_resource(2usize);
512
513 let result = world.run_system(mul_by_res, 3);
514 assert_eq!(result, 6);
515
516 let mut n = 3;
517 world.run_system(sys_with_ref_in, &mut n)
518 }
519
520 #[test]
521 fn system_replace_resource() {
522 #[derive(Default, HasSchema, Clone, PartialEq, Eq, Debug)]
523 pub struct A;
524 #[derive(Default, HasSchema, Clone, Debug)]
525 pub struct B {
526 x: u32,
527 }
528 let world = World::default();
529 let my_system = (|_a: ResInit<A>, mut b: ResMutInit<B>| {
530 let b2 = B { x: 45 };
531 *b = b2;
532 })
533 .system();
534
535 assert!(world.resources.get_cell::<B>().borrow().is_none());
536 world.run_system(my_system, ());
537
538 let res = world.resource::<B>();
539 assert_eq!(res.x, 45);
540
541 let res = world.resource::<A>();
542 assert_eq!(*res, A);
543 }
544}