
# Objective - Fixes #14658. ## Solution - Added `on_unimplemented` Diagnostic for `IntoObserverSystem` calling out argument ordering in a `note` - Added an example to the documentation on `App::observe` to provide some explanation to users. ## Testing - Ran CI locally - Deliberately introduced a parameter order error in the `ecs/observers.rs` example as a test. --- ## Showcase <details> <summary>Error Before</summary> ``` error[E0277]: the trait bound `{closure@examples/ecs/observers.rs:19:13: 22:37}: IntoObserverSystem<_, _, _>` is not satisfied --> examples/ecs/observers.rs:19:13 | 18 | .observe( | ------- required by a bound introduced by this call 19 | / |mines: Query<&Mine>, 20 | | trigger: Trigger<ExplodeMines>, 21 | | index: Res<SpatialIndex>, 22 | | mut commands: Commands| { ... | 34 | | } 35 | | }, | |_____________^ the trait `bevy::prelude::IntoSystem<bevy::prelude::Trigger<'static, _, _>, (), _>` is not implemented for closure `{closure@examples/ecs/observers.rs:19:13: 22:37}`, which is required by `{closure@examples/ecs/observers.rs:19:13: 22:37}: IntoObserverSystem<_, _, _>` | = note: required for `{closure@examples/ecs/observers.rs:19:13: 22:37}` to implement `IntoObserverSystem<_, _, _>` note: required by a bound in `bevy::prelude::App::observe` --> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_app\src\app.rs:995:24 | 993 | pub fn observe<E: Event, B: Bundle, M>( | ------- required by a bound in this associated function 994 | &mut self, 995 | observer: impl IntoObserverSystem<E, B, M>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `App::observe` For more information about this error, try `rustc --explain E0277`. error: could not compile `bevy` (example "observers") due to 1 previous error ``` </details> <details> <summary>Error After</summary> ``` error[E0277]: `{closure@examples/ecs/observers.rs:19:13: 22:37}` cannot become an `ObserverSystem` --> examples/ecs/observers.rs:19:13 | 18 | .observe( | ------- required by a bound introduced by this call 19 | / |mines: Query<&Mine>, 20 | | trigger: Trigger<ExplodeMines>, 21 | | index: Res<SpatialIndex>, 22 | | mut commands: Commands| { ... | 34 | | } 35 | | }, | |_____________^ the trait `IntoObserverSystem` is not implemented | = help: the trait `bevy::prelude::IntoSystem<bevy::prelude::Trigger<'static, _, _>, (), _>` is not implemented for closure `{closure@examples/ecs/observers.rs:19:13: 22:37}`, which is required by `{closure@examples/ecs/observers.rs:19:13: 22:37}: IntoObserverSystem<_, _, _>` = note: for function `ObserverSystem`s, ensure the first argument is a `Trigger<T>` and any subsequent ones are `SystemParam` = note: required for `{closure@examples/ecs/observers.rs:19:13: 22:37}` to implement `IntoObserverSystem<_, _, _>` note: required by a bound in `bevy::prelude::App::observe` --> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_app\src\app.rs:1025:24 | 1023 | pub fn observe<E: Event, B: Bundle, M>( | ------- required by a bound in this associated function 1024 | &mut self, 1025 | observer: impl IntoObserverSystem<E, B, M>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `App::observe` For more information about this error, try `rustc --explain E0277`. error: could not compile `bevy` (example "observers") due to 1 previous error ``` </details>
122 lines
3.5 KiB
Rust
122 lines
3.5 KiB
Rust
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<E: 'static, B: Bundle, Out = ()>:
|
|
System<In = Trigger<'static, E, B>, Out = Out> + Send + 'static
|
|
{
|
|
}
|
|
|
|
impl<
|
|
E: 'static,
|
|
B: Bundle,
|
|
Out,
|
|
T: System<In = Trigger<'static, E, B>, Out = Out> + Send + 'static,
|
|
> ObserverSystem<E, B, Out> 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<T>` and any subsequent ones are `SystemParam`"
|
|
)]
|
|
pub trait IntoObserverSystem<E: 'static, B: Bundle, M, Out = ()>: Send + 'static {
|
|
/// The type of [`System`] that this instance converts into.
|
|
type System: ObserverSystem<E, B, Out>;
|
|
|
|
/// Turns this value into its corresponding [`System`].
|
|
fn into_system(this: Self) -> Self::System;
|
|
}
|
|
|
|
impl<
|
|
S: IntoSystem<Trigger<'static, E, B>, Out, M> + Send + 'static,
|
|
M,
|
|
Out,
|
|
E: 'static,
|
|
B: Bundle,
|
|
> IntoObserverSystem<E, B, M, Out> for S
|
|
where
|
|
S::System: ObserverSystem<E, B, Out>,
|
|
{
|
|
type System = <S as IntoSystem<Trigger<'static, E, B>, 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<E: 'static, B: Bundle, Out, Func: Send + Sync + 'static, $($param: SystemParam),*> SystemParamFunction<fn(Trigger<E, B>, $($param,)*)> for Func
|
|
where
|
|
for <'a> &'a mut Func:
|
|
FnMut(Trigger<E, B>, $($param),*) -> Out +
|
|
FnMut(Trigger<E, B>, $(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<E: 'static, B: Bundle, Out, $($param,)*>(
|
|
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<TriggerEvent>) {}
|
|
fn b() {}
|
|
|
|
let mut world = World::new();
|
|
world.observe(a.pipe(b));
|
|
}
|
|
|
|
#[test]
|
|
fn test_piped_observer_systems_with_inputs() {
|
|
fn a(_: Trigger<TriggerEvent>) -> u32 {
|
|
3
|
|
}
|
|
fn b(_: In<u32>) {}
|
|
|
|
let mut world = World::new();
|
|
world.observe(a.pipe(b));
|
|
}
|
|
}
|