diff --git a/crates/bevy_ecs/src/schedule/condition.rs b/crates/bevy_ecs/src/schedule/condition.rs index e99eab07f9..e41fcd7230 100644 --- a/crates/bevy_ecs/src/schedule/condition.rs +++ b/crates/bevy_ecs/src/schedule/condition.rs @@ -1115,11 +1115,11 @@ pub mod common_conditions { CIn: SystemInput, C: Condition, { - condition.pipe(|In(new): In, mut prev: Local| { + IntoSystem::into_system(condition.pipe(|In(new): In, mut prev: Local| { let changed = *prev != new; *prev = new; changed - }) + })) } /// Generates a [`Condition`] that returns true when the result of @@ -1171,11 +1171,13 @@ pub mod common_conditions { CIn: SystemInput, C: Condition, { - condition.pipe(move |In(new): In, mut prev: Local| -> bool { - let now_true = *prev != new && new == to; - *prev = new; - now_true - }) + IntoSystem::into_system(condition.pipe( + move |In(new): In, mut prev: Local| -> bool { + let now_true = *prev != new && new == to; + *prev = new; + now_true + }, + )) } } diff --git a/crates/bevy_ecs/src/system/adapter_system.rs b/crates/bevy_ecs/src/system/adapter_system.rs index 2c7aea84bf..8c4badd995 100644 --- a/crates/bevy_ecs/src/system/adapter_system.rs +++ b/crates/bevy_ecs/src/system/adapter_system.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use super::{ReadOnlySystem, System}; +use super::{IntoSystem, ReadOnlySystem, System}; use crate::{ schedule::InternedSystemSet, system::{input::SystemInput, SystemIn}, @@ -62,6 +62,40 @@ pub trait Adapt: Send + Sync + 'static { ) -> Self::Out; } +/// An [`IntoSystem`] creating an instance of [`AdapterSystem`]. +#[derive(Clone)] +pub struct IntoAdapterSystem { + func: Func, + system: S, +} + +impl IntoAdapterSystem { + /// Creates a new [`IntoSystem`] that uses `func` to adapt `system`, via the [`Adapt`] trait. + pub const fn new(func: Func, system: S) -> Self { + Self { func, system } + } +} + +#[doc(hidden)] +pub struct IsAdapterSystemMarker; + +impl IntoSystem + for IntoAdapterSystem +where + Func: Adapt, + I: SystemInput, + S: IntoSystem, +{ + type System = AdapterSystem; + + // Required method + fn into_system(this: Self) -> Self::System { + let system = IntoSystem::into_system(this.system); + let name = system.name(); + AdapterSystem::new(this.func, system, name) + } +} + /// A [`System`] that takes the output of `S` and transforms it by applying `Func` to it. #[derive(Clone)] pub struct AdapterSystem { diff --git a/crates/bevy_ecs/src/system/combinator.rs b/crates/bevy_ecs/src/system/combinator.rs index 5434a0bb2d..f7bc133129 100644 --- a/crates/bevy_ecs/src/system/combinator.rs +++ b/crates/bevy_ecs/src/system/combinator.rs @@ -10,7 +10,7 @@ use crate::{ world::unsafe_world_cell::UnsafeWorldCell, }; -use super::{ReadOnlySystem, System}; +use super::{IntoSystem, ReadOnlySystem, System}; /// Customizes the behavior of a [`CombinatorSystem`]. /// @@ -273,6 +273,40 @@ where } } +/// An [`IntoSystem`] creating an instance of [`PipeSystem`]. +pub struct IntoPipeSystem { + a: A, + b: B, +} + +impl IntoPipeSystem { + /// Creates a new [`IntoSystem`] that pipes two inner systems. + pub const fn new(a: A, b: B) -> Self { + Self { a, b } + } +} + +#[doc(hidden)] +pub struct IsPipeSystemMarker; + +impl IntoSystem + for IntoPipeSystem +where + IA: SystemInput, + A: IntoSystem, + B: IntoSystem, + for<'a> IB: SystemInput = OA>, +{ + type System = PipeSystem; + + fn into_system(this: Self) -> Self::System { + let system_a = IntoSystem::into_system(this.a); + let system_b = IntoSystem::into_system(this.b); + let name = format!("Pipe({}, {})", system_a.name(), system_b.name()); + PipeSystem::new(system_a, system_b, Cow::Owned(name)) + } +} + /// A [`System`] created by piping the output of the first system into the input of the second. /// /// This can be repeated indefinitely, but system pipes cannot branch: the output is consumed by the receiving system. @@ -296,7 +330,7 @@ where /// world.insert_resource(Message("42".to_string())); /// /// // pipe the `parse_message_system`'s output into the `filter_system`s input -/// let mut piped_system = parse_message_system.pipe(filter_system); +/// let mut piped_system = IntoSystem::into_system(parse_message_system.pipe(filter_system)); /// piped_system.initialize(&mut world); /// assert_eq!(piped_system.run((), &mut world), Some(42)); /// } diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index a3bbf8eb37..07be635ec6 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -786,7 +786,10 @@ impl<'w, 's> Commands<'w, 's> { /// [`CachedSystemId`](crate::system::CachedSystemId) resource. /// /// See [`World::register_system_cached`] for more information. - pub fn run_system_cached + 'static>(&mut self, system: S) { + pub fn run_system_cached + Send + 'static>( + &mut self, + system: S, + ) { self.run_system_cached_with(system, ()); } @@ -798,7 +801,7 @@ impl<'w, 's> Commands<'w, 's> { where I: SystemInput: Send> + Send + 'static, M: 'static, - S: IntoSystem + 'static, + S: IntoSystem + Send + 'static, { self.queue(RunSystemCachedWith::new(system, input)); } diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index c84dcf6102..f441163447 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -117,7 +117,7 @@ mod system_name; mod system_param; mod system_registry; -use std::{any::TypeId, borrow::Cow}; +use std::any::TypeId; pub use adapter_system::*; pub use builder::*; @@ -168,16 +168,13 @@ pub trait IntoSystem: Sized { /// /// The second system must have [`In`](crate::system::In) as its first parameter, /// where `T` is the return type of the first system. - fn pipe(self, system: B) -> PipeSystem + fn pipe(self, system: B) -> IntoPipeSystem where Out: 'static, B: IntoSystem, for<'a> BIn: SystemInput = Out>, { - let system_a = IntoSystem::into_system(self); - let system_b = IntoSystem::into_system(system); - let name = format!("Pipe({}, {})", system_a.name(), system_b.name()); - PipeSystem::new(system_a, system_b, Cow::Owned(name)) + IntoPipeSystem::new(self, system) } /// Pass the output of this system into the passed function `f`, creating a new system that @@ -199,13 +196,11 @@ pub trait IntoSystem: Sized { /// # Err(()) /// } /// ``` - fn map(self, f: F) -> AdapterSystem + fn map(self, f: F) -> IntoAdapterSystem where F: Send + Sync + 'static + FnMut(Out) -> T, { - let system = Self::into_system(self); - let name = system.name(); - AdapterSystem::new(f, system, name) + IntoAdapterSystem::new(f, self) } /// Get the [`TypeId`] of the [`System`] produced after calling [`into_system`](`IntoSystem::into_system`). @@ -1680,7 +1675,7 @@ mod tests { let mut world = World::new(); world.init_resource::(); - let mut sys = first.pipe(second); + let mut sys = IntoSystem::into_system(first.pipe(second)); sys.initialize(&mut world); sys.run(default(), &mut world); diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index be08995cd9..91791b9571 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -1,8 +1,10 @@ +use std::marker::PhantomData; + use crate::{ bundle::Bundle, change_detection::Mut, entity::Entity, - system::{input::SystemInput, BoxedSystem, IntoSystem, System, SystemIn}, + system::{input::SystemInput, BoxedSystem, IntoSystem, System}, world::{Command, World}, {self as bevy_ecs}, }; @@ -48,7 +50,7 @@ impl RemovedSystem { /// and are created via [`World::register_system`]. pub struct SystemId { pub(crate) entity: Entity, - pub(crate) marker: std::marker::PhantomData O>, + pub(crate) marker: PhantomData O>, } impl SystemId { @@ -69,7 +71,7 @@ impl SystemId { pub fn from_entity(entity: Entity) -> Self { Self { entity, - marker: std::marker::PhantomData, + marker: PhantomData, } } } @@ -536,28 +538,38 @@ where /// [`Commands`](crate::system::Commands). /// /// See [`World::register_system_cached`] for more information. -pub struct RunSystemCachedWith> { +pub struct RunSystemCachedWith +where + I: SystemInput, + S: IntoSystem, +{ system: S, - input: SystemIn<'static, S>, + input: I::Inner<'static>, + _phantom: PhantomData<(fn() -> O, fn() -> M)>, } -impl> RunSystemCachedWith { +impl RunSystemCachedWith +where + I: SystemInput, + S: IntoSystem, +{ /// Creates a new [`Command`] struct, which can be added to /// [`Commands`](crate::system::Commands). - pub fn new( - system: impl IntoSystem, - input: SystemIn<'static, S>, - ) -> Self { + pub fn new(system: S, input: I::Inner<'static>) -> Self { Self { - system: IntoSystem::into_system(system), + system, input, + _phantom: PhantomData, } } } -impl> Command for RunSystemCachedWith +impl Command for RunSystemCachedWith where - S::In: SystemInput: Send>, + I: SystemInput: Send> + Send + 'static, + O: Send + 'static, + S: IntoSystem + Send + 'static, + M: 'static, { fn apply(self, world: &mut World) { let _ = world.run_system_cached_with(self.system, self.input); @@ -824,6 +836,40 @@ mod tests { assert!(matches!(output, Ok(x) if x == four())); } + #[test] + fn cached_system_commands() { + fn sys(mut counter: ResMut) { + counter.0 = 1; + } + + let mut world = World::new(); + world.insert_resource(Counter(0)); + + world.commands().run_system_cached(sys); + world.flush_commands(); + + assert_eq!(world.resource::().0, 1); + } + + #[test] + fn cached_system_adapters() { + fn four() -> i32 { + 4 + } + + fn double(In(i): In) -> i32 { + i * 2 + } + + let mut world = World::new(); + + let output = world.run_system_cached(four.pipe(double)); + assert!(matches!(output, Ok(8))); + + let output = world.run_system_cached(four.map(|i| i * 2)); + assert!(matches!(output, Ok(8))); + } + #[test] fn system_with_input_ref() { fn with_ref(InRef(input): InRef, mut counter: ResMut) {