mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 06:00:20 +00:00
Rename system chaining to system piping (#6230)
# Objective > System chaining is a confusing name: it implies the ability to construct non-linear graphs, and suggests a sense of system ordering that is only incidentally true. Instead, it actually works by passing data from one system to the next, much like the pipe operator. > In the accepted [stageless RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/45-stageless.md), this concept is renamed to piping, and "system chaining" is used to construct groups of systems with ordering dependencies between them. Fixes #6225. ## Changelog System chaining has been renamed to system piping to improve clarity (and free up the name for new ordering APIs). ## Migration Guide The `.chain(handler_system)` method on systems is now `.pipe(handler_system)`. The `IntoChainSystem` trait is now `IntoPipeSystem`, and the `ChainSystem` struct is now `PipeSystem`.
This commit is contained in:
parent
6ce7ce208e
commit
c0a93aa7a4
8 changed files with 62 additions and 60 deletions
10
Cargo.toml
10
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
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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<T>>| {
|
||||
state.stack.last().unwrap() == &pred && state.transition.is_none()
|
||||
})
|
||||
.chain(should_run_adapter::<T>)
|
||||
.pipe(should_run_adapter::<T>)
|
||||
.after(DriverLabel::of::<T>())
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ where
|
|||
Some(_) => false,
|
||||
None => *is_inactive,
|
||||
})
|
||||
.chain(should_run_adapter::<T>)
|
||||
.pipe(should_run_adapter::<T>)
|
||||
.after(DriverLabel::of::<T>())
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ where
|
|||
Some(_) => false,
|
||||
None => *is_in_stack,
|
||||
})
|
||||
.chain(should_run_adapter::<T>)
|
||||
.pipe(should_run_adapter::<T>)
|
||||
.after(DriverLabel::of::<T>())
|
||||
}
|
||||
|
||||
|
@ -133,7 +133,7 @@ where
|
|||
_ => false,
|
||||
})
|
||||
})
|
||||
.chain(should_run_adapter::<T>)
|
||||
.pipe(should_run_adapter::<T>)
|
||||
.after(DriverLabel::of::<T>())
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,7 @@ where
|
|||
_ => false,
|
||||
})
|
||||
})
|
||||
.chain(should_run_adapter::<T>)
|
||||
.pipe(should_run_adapter::<T>)
|
||||
.after(DriverLabel::of::<T>())
|
||||
}
|
||||
|
||||
|
@ -162,7 +162,7 @@ where
|
|||
_ => false,
|
||||
})
|
||||
})
|
||||
.chain(should_run_adapter::<T>)
|
||||
.pipe(should_run_adapter::<T>)
|
||||
.after(DriverLabel::of::<T>())
|
||||
}
|
||||
|
||||
|
@ -176,7 +176,7 @@ where
|
|||
_ => false,
|
||||
})
|
||||
})
|
||||
.chain(should_run_adapter::<T>)
|
||||
.pipe(should_run_adapter::<T>)
|
||||
.after(DriverLabel::of::<T>())
|
||||
}
|
||||
|
||||
|
|
|
@ -499,7 +499,7 @@ impl<T> Copy for SystemTypeIdLabel<T> {}
|
|||
///
|
||||
/// # 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<T> Copy for SystemTypeIdLabel<T> {}
|
|||
/// // 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<AIn, Shared, BOut, A, AParam, AMarker, B, BParam, BMarker>(
|
||||
/// /// Pipe creates a new system which calls `a`, then calls `b` with the output of `a`
|
||||
/// pub fn pipe<AIn, Shared, BOut, A, AParam, AMarker, B, BParam, BMarker>(
|
||||
/// mut a: A,
|
||||
/// mut b: B,
|
||||
/// ) -> impl FnMut(In<AIn>, ParamSet<(SystemParamItem<AParam>, SystemParamItem<BParam>)>) -> BOut
|
||||
|
@ -529,15 +529,15 @@ impl<T> Copy for SystemTypeIdLabel<T> {}
|
|||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // 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<T> Copy for SystemTypeIdLabel<T> {}
|
|||
/// result.ok().filter(|&n| n < 100)
|
||||
/// }
|
||||
/// ```
|
||||
/// [`ChainSystem`]: crate::system::ChainSystem
|
||||
/// [`PipeSystem`]: crate::system::PipeSystem
|
||||
/// [`ParamSet`]: crate::system::ParamSet
|
||||
pub trait SystemParamFunction<In, Out, Param: SystemParam, Marker>: Send + Sync + 'static {
|
||||
fn run(&mut self, input: In, param_value: SystemParamItem<Param>) -> Out;
|
||||
|
|
|
@ -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
|
||||
///
|
||||
|
|
|
@ -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<SystemA, SystemB> {
|
||||
pub struct PipeSystem<SystemA, SystemB> {
|
||||
system_a: SystemA,
|
||||
system_b: SystemB,
|
||||
name: Cow<'static, str>,
|
||||
|
@ -53,7 +54,7 @@ pub struct ChainSystem<SystemA, SystemB> {
|
|||
archetype_component_access: Access<ArchetypeComponentId>,
|
||||
}
|
||||
|
||||
impl<SystemA: System, SystemB: System<In = SystemA::Out>> System for ChainSystem<SystemA, SystemB> {
|
||||
impl<SystemA: System, SystemB: System<In = SystemA::Out>> System for PipeSystem<SystemA, SystemB> {
|
||||
type In = SystemA::In;
|
||||
type Out = SystemB::Out;
|
||||
|
||||
|
@ -121,33 +122,34 @@ impl<SystemA: System, SystemB: System<In = SystemA::Out>> 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.
|
||||
///
|
||||
/// This trait is blanket implemented for all system pairs that fulfill the chaining requirement.
|
||||
/// The first system must have return type `T`
|
||||
/// and the second system must have [`In<T>`](crate::system::In) as its first system parameter.
|
||||
///
|
||||
/// See [`ChainSystem`].
|
||||
pub trait IntoChainSystem<ParamA, Payload, SystemB, ParamB, Out>:
|
||||
/// This trait is blanket implemented for all system pairs that fulfill the type requirements.
|
||||
///
|
||||
/// See [`PipeSystem`].
|
||||
pub trait IntoPipeSystem<ParamA, Payload, SystemB, ParamB, Out>:
|
||||
IntoSystem<(), Payload, ParamA> + Sized
|
||||
where
|
||||
SystemB: IntoSystem<Payload, Out, ParamB>,
|
||||
{
|
||||
/// 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<Self::System, SystemB::System>;
|
||||
/// Pass the output of this system `A` into a second system `B`, creating a new compound system.
|
||||
fn pipe(self, system: SystemB) -> PipeSystem<Self::System, SystemB::System>;
|
||||
}
|
||||
|
||||
impl<SystemA, ParamA, Payload, SystemB, ParamB, Out>
|
||||
IntoChainSystem<ParamA, Payload, SystemB, ParamB, Out> for SystemA
|
||||
IntoPipeSystem<ParamA, Payload, SystemB, ParamB, Out> for SystemA
|
||||
where
|
||||
SystemA: IntoSystem<(), Payload, ParamA>,
|
||||
SystemB: IntoSystem<Payload, Out, ParamB>,
|
||||
{
|
||||
fn chain(self, system: SystemB) -> ChainSystem<SystemA::System, SystemB::System> {
|
||||
fn pipe(self, system: SystemB) -> PipeSystem<SystemA::System, SystemB::System> {
|
||||
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<impl std::fmt::Debug>) {
|
||||
|
@ -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::<Result<u32, std::io::Error>>.chain(unwrap));
|
||||
assert_is_system(returning::<Option<()>>.chain(ignore));
|
||||
assert_is_system(returning::<&str>.chain(new(u64::from_str)).chain(unwrap));
|
||||
assert_is_system(returning::<Result<u32, std::io::Error>>.pipe(unwrap));
|
||||
assert_is_system(returning::<Option<()>>.pipe(ignore));
|
||||
assert_is_system(returning::<&str>.pipe(new(u64::from_str)).pipe(unwrap));
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in a new issue