use bevy_utils::all_tuples; use crate::{ prelude::{Bundle, Trigger}, system::{System, SystemParam, SystemParamFunction, SystemParamItem}, }; use super::IntoSystem; /// Implemented for systems that have an [`Observer`] as the first argument. /// /// [`Observer`]: crate::observer::Observer pub trait ObserverSystem: System, Out = Out> + Send + 'static { } impl< E: 'static, B: Bundle, Out, T: System, Out = Out> + Send + 'static, > ObserverSystem for T { } /// Implemented for systems that convert into [`ObserverSystem`]. #[diagnostic::on_unimplemented( message = "`{Self}` cannot become an `ObserverSystem`", label = "the trait `IntoObserverSystem` is not implemented", note = "for function `ObserverSystem`s, ensure the first argument is a `Trigger` and any subsequent ones are `SystemParam`" )] pub trait IntoObserverSystem: Send + 'static { /// The type of [`System`] that this instance converts into. type System: ObserverSystem; /// Turns this value into its corresponding [`System`]. fn into_system(this: Self) -> Self::System; } impl< S: IntoSystem, Out, M> + Send + 'static, M, Out, E: 'static, B: Bundle, > IntoObserverSystem for S where S::System: ObserverSystem, { type System = , Out, M>>::System; fn into_system(this: Self) -> Self::System { IntoSystem::into_system(this) } } macro_rules! impl_system_function { ($($param: ident),*) => { #[allow(non_snake_case)] impl SystemParamFunction, $($param,)*)> for Func where for <'a> &'a mut Func: FnMut(Trigger, $($param),*) -> Out + FnMut(Trigger, $(SystemParamItem<$param>),*) -> Out, Out: 'static { type In = Trigger<'static, E, B>; type Out = Out; type Param = ($($param,)*); #[inline] fn run(&mut self, input: Trigger<'static, E, B>, param_value: SystemParamItem< ($($param,)*)>) -> Out { #[allow(clippy::too_many_arguments)] fn call_inner( mut f: impl FnMut(Trigger<'static, E, B>, $($param,)*) -> Out, input: Trigger<'static, E, B>, $($param: $param,)* ) -> Out{ f(input, $($param,)*) } let ($($param,)*) = param_value; call_inner(self, input, $($param),*) } } } } all_tuples!(impl_system_function, 0, 16, F); #[cfg(test)] mod tests { use crate::{ self as bevy_ecs, event::Event, observer::Trigger, system::{In, IntoSystem}, world::World, }; #[derive(Event)] struct TriggerEvent; #[test] fn test_piped_observer_systems_no_input() { fn a(_: Trigger) {} fn b() {} let mut world = World::new(); world.observe(a.pipe(b)); } #[test] fn test_piped_observer_systems_with_inputs() { fn a(_: Trigger) -> u32 { 3 } fn b(_: In) {} let mut world = World::new(); world.observe(a.pipe(b)); } }