diff --git a/Cargo.toml b/Cargo.toml index 6612b1630986e..560ae5807f43c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -872,12 +872,12 @@ category = "ECS (Entity Component System)" wasm = false [[example]] -name = "system_chaining" -path = "examples/ecs/system_chaining.rs" +name = "system_piping" +path = "examples/ecs/system_piping.rs" -[package.metadata.example.system_chaining] -name = "System Chaining" -description = "Chain two systems together, specifying a return type in a system (such as `Result`)" +[package.metadata.example.system_piping] +name = "System Piping" +description = "Pipe the output of one system into a second, allowing you to handle any errors gracefully" category = "ECS (Entity Component System)" wasm = false diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 6143036737dbb..90fd426e43d72 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -38,7 +38,7 @@ pub mod prelude { Schedule, Stage, StageLabel, State, SystemLabel, SystemSet, SystemStage, }, system::{ - adapter as system_adapter, Commands, In, IntoChainSystem, IntoSystem, Local, NonSend, + adapter as system_adapter, Commands, In, IntoPipeSystem, IntoSystem, Local, NonSend, NonSendMut, ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut, Resource, System, SystemParamFunction, }, diff --git a/crates/bevy_ecs/src/schedule/state.rs b/crates/bevy_ecs/src/schedule/state.rs index 3037500b6caa6..3ad5dbef5f059 100644 --- a/crates/bevy_ecs/src/schedule/state.rs +++ b/crates/bevy_ecs/src/schedule/state.rs @@ -3,7 +3,7 @@ use crate::{ RunCriteriaDescriptor, RunCriteriaDescriptorCoercion, RunCriteriaLabel, ShouldRun, SystemSet, }, - system::{In, IntoChainSystem, Local, Res, ResMut, Resource}, + system::{In, IntoPipeSystem, Local, Res, ResMut, Resource}, }; use std::{ any::TypeId, @@ -79,7 +79,7 @@ where (move |state: Res>| { state.stack.last().unwrap() == &pred && state.transition.is_none() }) - .chain(should_run_adapter::) + .pipe(should_run_adapter::) .after(DriverLabel::of::()) } @@ -95,7 +95,7 @@ where Some(_) => false, None => *is_inactive, }) - .chain(should_run_adapter::) + .pipe(should_run_adapter::) .after(DriverLabel::of::()) } @@ -118,7 +118,7 @@ where Some(_) => false, None => *is_in_stack, }) - .chain(should_run_adapter::) + .pipe(should_run_adapter::) .after(DriverLabel::of::()) } @@ -133,7 +133,7 @@ where _ => false, }) }) - .chain(should_run_adapter::) + .pipe(should_run_adapter::) .after(DriverLabel::of::()) } @@ -148,7 +148,7 @@ where _ => false, }) }) - .chain(should_run_adapter::) + .pipe(should_run_adapter::) .after(DriverLabel::of::()) } @@ -162,7 +162,7 @@ where _ => false, }) }) - .chain(should_run_adapter::) + .pipe(should_run_adapter::) .after(DriverLabel::of::()) } @@ -176,7 +176,7 @@ where _ => false, }) }) - .chain(should_run_adapter::) + .pipe(should_run_adapter::) .after(DriverLabel::of::()) } diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index 5cbaa6d88c379..c3f672e718734 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -499,7 +499,7 @@ impl Copy for SystemTypeIdLabel {} /// /// # Example /// -/// To create something like [`ChainSystem`], but in entirely safe code. +/// To create something like [`PipeSystem`], but in entirely safe code. /// /// ```rust /// use std::num::ParseIntError; @@ -510,8 +510,8 @@ impl Copy for SystemTypeIdLabel {} /// // Unfortunately, we need all of these generics. `A` is the first system, with its /// // parameters and marker type required for coherence. `B` is the second system, and /// // the other generics are for the input/output types of `A` and `B`. -/// /// Chain creates a new system which calls `a`, then calls `b` with the output of `a` -/// pub fn chain( +/// /// Pipe creates a new system which calls `a`, then calls `b` with the output of `a` +/// pub fn pipe( /// mut a: A, /// mut b: B, /// ) -> impl FnMut(In, ParamSet<(SystemParamItem, SystemParamItem)>) -> BOut @@ -529,15 +529,15 @@ impl Copy for SystemTypeIdLabel {} /// } /// } /// -/// // Usage example for `chain`: +/// // Usage example for `pipe`: /// fn main() { /// let mut world = World::default(); /// world.insert_resource(Message("42".to_string())); /// -/// // chain the `parse_message_system`'s output into the `filter_system`s input -/// let mut chained_system = IntoSystem::into_system(chain(parse_message, filter)); -/// chained_system.initialize(&mut world); -/// assert_eq!(chained_system.run((), &mut world), Some(42)); +/// // pipe the `parse_message_system`'s output into the `filter_system`s input +/// let mut piped_system = IntoSystem::into_system(pipe(parse_message, filter)); +/// piped_system.initialize(&mut world); +/// assert_eq!(piped_system.run((), &mut world), Some(42)); /// } /// /// #[derive(Resource)] @@ -551,7 +551,7 @@ impl Copy for SystemTypeIdLabel {} /// result.ok().filter(|&n| n < 100) /// } /// ``` -/// [`ChainSystem`]: crate::system::ChainSystem +/// [`PipeSystem`]: crate::system::PipeSystem /// [`ParamSet`]: crate::system::ParamSet pub trait SystemParamFunction: Send + Sync + 'static { fn run(&mut self, input: In, param_value: SystemParamItem) -> Out; diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index bb177a279ac2e..2243636e3182f 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -75,8 +75,8 @@ mod function_system; mod query; #[allow(clippy::module_inception)] mod system; -mod system_chaining; mod system_param; +mod system_piping; pub use commands::*; pub use exclusive_function_system::*; @@ -84,8 +84,8 @@ pub use exclusive_system_param::*; pub use function_system::*; pub use query::*; pub use system::*; -pub use system_chaining::*; pub use system_param::*; +pub use system_piping::*; /// Ensure that a given function is a system /// diff --git a/crates/bevy_ecs/src/system/system_chaining.rs b/crates/bevy_ecs/src/system/system_piping.rs similarity index 79% rename from crates/bevy_ecs/src/system/system_chaining.rs rename to crates/bevy_ecs/src/system/system_piping.rs index 17e0c0455bc39..4f4a192ff535d 100644 --- a/crates/bevy_ecs/src/system/system_chaining.rs +++ b/crates/bevy_ecs/src/system/system_piping.rs @@ -7,10 +7,11 @@ use crate::{ }; use std::borrow::Cow; -/// A [`System`] that chains two systems together, creating a new system that routes the output of -/// the first system into the input of the second system, yielding the output of the second system. +/// A [`System`] created by piping the output of the first system into the input of the second. /// -/// Given two systems `A` and `B`, A may be chained with `B` as `A.chain(B)` if the output type of `A` is +/// This can be repeated indefinitely, but system pipes cannot branch: the output is consumed by the receiving system. +/// +/// Given two systems `A` and `B`, A may be piped into `B` as `A.pipe(B)` if the output type of `A` is /// equal to the input type of `B`. /// /// Note that for [`FunctionSystem`](crate::system::FunctionSystem)s the output is the return value @@ -28,10 +29,10 @@ use std::borrow::Cow; /// let mut world = World::default(); /// world.insert_resource(Message("42".to_string())); /// -/// // chain the `parse_message_system`'s output into the `filter_system`s input -/// let mut chained_system = parse_message_system.chain(filter_system); -/// chained_system.initialize(&mut world); -/// assert_eq!(chained_system.run((), &mut world), Some(42)); +/// // pipe the `parse_message_system`'s output into the `filter_system`s input +/// let mut piped_system = parse_message_system.pipe(filter_system); +/// piped_system.initialize(&mut world); +/// assert_eq!(piped_system.run((), &mut world), Some(42)); /// } /// /// #[derive(Resource)] @@ -45,7 +46,7 @@ use std::borrow::Cow; /// result.ok().filter(|&n| n < 100) /// } /// ``` -pub struct ChainSystem { +pub struct PipeSystem { system_a: SystemA, system_b: SystemB, name: Cow<'static, str>, @@ -53,7 +54,7 @@ pub struct ChainSystem { archetype_component_access: Access, } -impl> System for ChainSystem { +impl> System for PipeSystem { type In = SystemA::In; type Out = SystemB::Out; @@ -121,33 +122,34 @@ impl> System for ChainSystem } } -/// An extension trait providing the [`IntoChainSystem::chain`] method for convenient [`System`] -/// chaining. +/// An extension trait providing the [`IntoPipeSystem::pipe`] method to pass input from one system into the next. +/// +/// The first system must have return type `T` +/// and the second system must have [`In`](crate::system::In) as its first system parameter. /// -/// This trait is blanket implemented for all system pairs that fulfill the chaining requirement. +/// This trait is blanket implemented for all system pairs that fulfill the type requirements. /// -/// See [`ChainSystem`]. -pub trait IntoChainSystem: +/// See [`PipeSystem`]. +pub trait IntoPipeSystem: IntoSystem<(), Payload, ParamA> + Sized where SystemB: IntoSystem, { - /// Chain this system `A` with another system `B` creating a new system that feeds system A's - /// output into system `B`, returning the output of system `B`. - fn chain(self, system: SystemB) -> ChainSystem; + /// Pass the output of this system `A` into a second system `B`, creating a new compound system. + fn pipe(self, system: SystemB) -> PipeSystem; } impl - IntoChainSystem for SystemA + IntoPipeSystem for SystemA where SystemA: IntoSystem<(), Payload, ParamA>, SystemB: IntoSystem, { - fn chain(self, system: SystemB) -> ChainSystem { + fn pipe(self, system: SystemB) -> PipeSystem { let system_a = IntoSystem::into_system(self); let system_b = IntoSystem::into_system(system); - ChainSystem { - name: Cow::Owned(format!("Chain({}, {})", system_a.name(), system_b.name())), + PipeSystem { + name: Cow::Owned(format!("Pipe({}, {})", system_a.name(), system_b.name())), system_a, system_b, archetype_component_access: Default::default(), @@ -156,7 +158,7 @@ where } } -/// A collection of common adapters for [chaining](super::ChainSystem) the result of a system. +/// A collection of common adapters for [piping](super::PipeSystem) the result of a system. pub mod adapter { use crate::system::In; use std::fmt::Debug; @@ -168,9 +170,9 @@ pub mod adapter { /// use bevy_ecs::prelude::*; /// /// return1 - /// .chain(system_adapter::new(u32::try_from)) - /// .chain(system_adapter::unwrap) - /// .chain(print); + /// .pipe(system_adapter::new(u32::try_from)) + /// .pipe(system_adapter::unwrap) + /// .pipe(print); /// /// fn return1() -> u64 { 1 } /// fn print(In(x): In) { @@ -204,7 +206,7 @@ pub mod adapter { /// .add_system_to_stage( /// CoreStage::Update, /// // Panic if the load system returns an error. - /// load_save_system.chain(system_adapter::unwrap) + /// load_save_system.pipe(system_adapter::unwrap) /// ) /// // ... /// # ; @@ -224,7 +226,7 @@ pub mod adapter { res.unwrap() } - /// System adapter that ignores the output of the previous system in a chain. + /// System adapter that ignores the output of the previous system in a pipe. /// This is useful for fallible systems that should simply return early in case of an `Err`/`None`. /// /// # Examples @@ -248,7 +250,7 @@ pub mod adapter { /// .add_system_to_stage( /// CoreStage::Update, /// // If the system fails, just move on and try again next frame. - /// fallible_system.chain(system_adapter::ignore) + /// fallible_system.pipe(system_adapter::ignore) /// ) /// // ... /// # ; @@ -278,8 +280,8 @@ pub mod adapter { unimplemented!() } - assert_is_system(returning::>.chain(unwrap)); - assert_is_system(returning::>.chain(ignore)); - assert_is_system(returning::<&str>.chain(new(u64::from_str)).chain(unwrap)); + assert_is_system(returning::>.pipe(unwrap)); + assert_is_system(returning::>.pipe(ignore)); + assert_is_system(returning::<&str>.pipe(new(u64::from_str)).pipe(unwrap)); } } diff --git a/examples/README.md b/examples/README.md index 18853ac19db1a..4e105d66d9ee3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -199,9 +199,9 @@ Example | Description [Removal Detection](../examples/ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame [Startup System](../examples/ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up) [State](../examples/ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state -[System Chaining](../examples/ecs/system_chaining.rs) | Chain two systems together, specifying a return type in a system (such as `Result`) [System Closure](../examples/ecs/system_closure.rs) | Show how to use closures as systems, and how to configure `Local` variables by capturing external state [System Parameter](../examples/ecs/system_param.rs) | Illustrates creating custom system parameters with `SystemParam` +[System Piping](../examples/ecs/system_piping.rs) | Pipe the output of one system into a second, allowing you to handle any errors gracefully [System Sets](../examples/ecs/system_sets.rs) | Shows `SystemSet` use along with run criterion [Timers](../examples/ecs/timers.rs) | Illustrates ticking `Timer` resources inside systems and handling their state diff --git a/examples/ecs/system_chaining.rs b/examples/ecs/system_piping.rs similarity index 85% rename from examples/ecs/system_chaining.rs rename to examples/ecs/system_piping.rs index 8046dcd7ec3d1..ca023ab7afef3 100644 --- a/examples/ecs/system_chaining.rs +++ b/examples/ecs/system_piping.rs @@ -1,5 +1,5 @@ -//! Illustrates how to make a single system from multiple functions running in sequence and sharing -//! their inputs and outputs. +//! Illustrates how to make a single system from multiple functions running in sequence, +//! passing the output of the first into the input of the next. use anyhow::Result; use bevy::prelude::*; @@ -7,7 +7,7 @@ use bevy::prelude::*; fn main() { App::new() .insert_resource(Message("42".to_string())) - .add_system(parse_message_system.chain(handler_system)) + .add_system(parse_message_system.pipe(handler_system)) .run(); }