Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Rename system chaining to system piping #6230

Closed
wants to merge 11 commits into from
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -872,10 +872,10 @@ 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]
[package.metadata.example.system_piping]
name = "System Chaining"
description = "Chain two systems together, specifying a return type in a system (such as `Result`)"
alice-i-cecile marked this conversation as resolved.
Show resolved Hide resolved
category = "ECS (Entity Component System)"
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down
16 changes: 8 additions & 8 deletions crates/bevy_ecs/src/schedule/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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>())
}

Expand All @@ -95,7 +95,7 @@ where
Some(_) => false,
None => *is_inactive,
})
.chain(should_run_adapter::<T>)
.pipe(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
}

Expand All @@ -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>())
}

Expand All @@ -133,7 +133,7 @@ where
_ => false,
})
})
.chain(should_run_adapter::<T>)
.pipe(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
}

Expand All @@ -148,7 +148,7 @@ where
_ => false,
})
})
.chain(should_run_adapter::<T>)
.pipe(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
}

Expand All @@ -162,7 +162,7 @@ where
_ => false,
})
})
.chain(should_run_adapter::<T>)
.pipe(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
}

Expand All @@ -176,7 +176,7 @@ where
_ => false,
})
})
.chain(should_run_adapter::<T>)
.pipe(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
}

Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_ecs/src/system/function_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,9 +535,9 @@ impl<T> Copy for SystemTypeIdLabel<T> {}
/// 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));
/// let mut piped_system = IntoSystem::into_system(chain(parse_message, filter));
/// piped_system.initialize(&mut world);
/// assert_eq!(piped_system.run((), &mut world), Some(42));
/// }
///
/// #[derive(Resource)]
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,17 @@ 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::*;
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
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 indefintely, but system pipes cannot branch: the output is consumed by the receiving system.
alice-i-cecile marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(beginner question, not a review question)

Can a hint be provided as to what could be done instead of piping to allow giving the input of one system to multiple systems? This seems like something that would come up a lot.

The answer might well be: "you should pipe into an adapter system that calls non-system functions to operate on the piped input"? Or perhaps there is another mechanism provided by Stageless, whose docs there should be a link to?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing, unless @DJMcNab has something to say about it. You would have to use events or some other data flow mechanism instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Ultimately this shouldn't need to be special cased at all, but that's well outside of the scope of this PR).

///
/// 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
Expand All @@ -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)]
Expand All @@ -45,15 +46,15 @@ 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>,
component_access: Access<ComponentId>,
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;

Expand Down Expand Up @@ -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.
///
/// The first system must have a non-empty return type `T`
alice-i-cecile marked this conversation as resolved.
Show resolved Hide resolved
/// and the second system must have [`In<T>`](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<ParamA, Payload, SystemB, ParamB, Out>:
/// 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(),
Expand All @@ -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;
Expand All @@ -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>) {
Expand Down Expand Up @@ -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)
/// )
/// // ...
/// # ;
Expand All @@ -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
Expand All @@ -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)
/// )
/// // ...
/// # ;
Expand Down Expand Up @@ -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));
}
}
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ 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 Chaining](../examples/ecs/system_piping.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 Sets](../examples/ecs/system_sets.rs) | Shows `SystemSet` use along with run criterion
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
//! 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::*;

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();
}

Expand Down