bevy_ecs/system/
observer_system.rs

1use bevy_utils::all_tuples;
2
3use crate::{
4    prelude::{Bundle, Trigger},
5    system::{System, SystemParam, SystemParamFunction, SystemParamItem},
6};
7
8use super::IntoSystem;
9
10/// Implemented for systems that have an [`Observer`] as the first argument.
11///
12/// [`Observer`]: crate::observer::Observer
13pub trait ObserverSystem<E: 'static, B: Bundle, Out = ()>:
14    System<In = Trigger<'static, E, B>, Out = Out> + Send + 'static
15{
16}
17
18impl<
19        E: 'static,
20        B: Bundle,
21        Out,
22        T: System<In = Trigger<'static, E, B>, Out = Out> + Send + 'static,
23    > ObserverSystem<E, B, Out> for T
24{
25}
26
27/// Implemented for systems that convert into [`ObserverSystem`].
28pub trait IntoObserverSystem<E: 'static, B: Bundle, M, Out = ()>: Send + 'static {
29    /// The type of [`System`] that this instance converts into.
30    type System: ObserverSystem<E, B, Out>;
31
32    /// Turns this value into its corresponding [`System`].
33    fn into_system(this: Self) -> Self::System;
34}
35
36impl<
37        S: IntoSystem<Trigger<'static, E, B>, Out, M> + Send + 'static,
38        M,
39        Out,
40        E: 'static,
41        B: Bundle,
42    > IntoObserverSystem<E, B, M, Out> for S
43where
44    S::System: ObserverSystem<E, B, Out>,
45{
46    type System = <S as IntoSystem<Trigger<'static, E, B>, Out, M>>::System;
47
48    fn into_system(this: Self) -> Self::System {
49        IntoSystem::into_system(this)
50    }
51}
52
53macro_rules! impl_system_function {
54    ($($param: ident),*) => {
55        #[allow(non_snake_case)]
56        impl<E: 'static, B: Bundle, Out, Func: Send + Sync + 'static, $($param: SystemParam),*> SystemParamFunction<fn(Trigger<E, B>, $($param,)*)> for Func
57        where
58        for <'a> &'a mut Func:
59                FnMut(Trigger<E, B>, $($param),*) -> Out +
60                FnMut(Trigger<E, B>, $(SystemParamItem<$param>),*) -> Out, Out: 'static
61        {
62            type In = Trigger<'static, E, B>;
63            type Out = Out;
64            type Param = ($($param,)*);
65            #[inline]
66            fn run(&mut self, input: Trigger<'static, E, B>, param_value: SystemParamItem< ($($param,)*)>) -> Out {
67                #[allow(clippy::too_many_arguments)]
68                fn call_inner<E: 'static, B: Bundle, Out, $($param,)*>(
69                    mut f: impl FnMut(Trigger<'static, E, B>, $($param,)*) -> Out,
70                    input: Trigger<'static, E, B>,
71                    $($param: $param,)*
72                ) -> Out{
73                    f(input, $($param,)*)
74                }
75                let ($($param,)*) = param_value;
76                call_inner(self, input, $($param),*)
77            }
78        }
79    }
80}
81
82all_tuples!(impl_system_function, 0, 16, F);
83
84#[cfg(test)]
85mod tests {
86    use crate::{
87        self as bevy_ecs,
88        event::Event,
89        observer::Trigger,
90        system::{In, IntoSystem},
91        world::World,
92    };
93
94    #[derive(Event)]
95    struct TriggerEvent;
96
97    #[test]
98    fn test_piped_observer_systems_no_input() {
99        fn a(_: Trigger<TriggerEvent>) {}
100        fn b() {}
101
102        let mut world = World::new();
103        world.observe(a.pipe(b));
104    }
105
106    #[test]
107    fn test_piped_observer_systems_with_inputs() {
108        fn a(_: Trigger<TriggerEvent>) -> u32 {
109            3
110        }
111        fn b(_: In<u32>) {}
112
113        let mut world = World::new();
114        world.observe(a.pipe(b));
115    }
116}