Skip to content

Commit

Permalink
Reorganize system modules (bevyengine#8419)
Browse files Browse the repository at this point in the history
# Objective

Follow-up to bevyengine#8377.

As the system module has been refactored, there are many types that no
longer make sense to live in the files that they do:
- The `IntoSystem` trait is in `function_system.rs`, even though this
trait is relevant to all kinds of systems. Same for the `In<T>` type.
- `PipeSystem` is now just an implementation of `CombinatorSystem`, so
`system_piping.rs` no longer needs its own file.

## Solution

- Move `IntoSystem`, `In<T>`, and system piping combinators & tests into
the top-level `mod.rs` file for `bevy_ecs::system`.
- Move `PipeSystem` into `combinator.rs`.
  • Loading branch information
JoJoJet authored Apr 17, 2023
1 parent 764961b commit ce252f8
Show file tree
Hide file tree
Showing 4 changed files with 455 additions and 464 deletions.
62 changes: 62 additions & 0 deletions crates/bevy_ecs/src/system/combinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,65 @@ where
B: ReadOnlySystem,
{
}

/// 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.
///
/// 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
/// of the function and the input is the first [`SystemParam`](crate::system::SystemParam) if it is
/// tagged with [`In`](crate::system::In) or `()` if the function has no designated input parameter.
///
/// # Examples
///
/// ```
/// use std::num::ParseIntError;
///
/// use bevy_ecs::prelude::*;
///
/// fn main() {
/// let mut world = World::default();
/// 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);
/// piped_system.initialize(&mut world);
/// assert_eq!(piped_system.run((), &mut world), Some(42));
/// }
///
/// #[derive(Resource)]
/// struct Message(String);
///
/// fn parse_message_system(message: Res<Message>) -> Result<usize, ParseIntError> {
/// message.0.parse::<usize>()
/// }
///
/// fn filter_system(In(result): In<Result<usize, ParseIntError>>) -> Option<usize> {
/// result.ok().filter(|&n| n < 100)
/// }
/// ```
pub type PipeSystem<SystemA, SystemB> = CombinatorSystem<Pipe, SystemA, SystemB>;

#[doc(hidden)]
pub struct Pipe;

impl<A, B> Combine<A, B> for Pipe
where
A: System,
B: System<In = A::Out>,
{
type In = A::In;
type Out = B::Out;

fn combine(
input: Self::In,
a: impl FnOnce(A::In) -> A::Out,
b: impl FnOnce(B::In) -> B::Out,
) -> Self::Out {
let value = a(input);
b(value)
}
}
75 changes: 1 addition & 74 deletions crates/bevy_ecs/src/system/function_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
use bevy_utils::all_tuples;
use std::{any::TypeId, borrow::Cow, marker::PhantomData};

use super::{PipeSystem, ReadOnlySystem};
use super::{In, IntoSystem, ReadOnlySystem};

/// The metadata of a [`System`].
#[derive(Clone)]
Expand Down Expand Up @@ -308,79 +308,6 @@ impl<Param: SystemParam> FromWorld for SystemState<Param> {
}
}

/// Conversion trait to turn something into a [`System`].
///
/// Use this to get a system from a function. Also note that every system implements this trait as
/// well.
///
/// # Examples
///
/// ```
/// use bevy_ecs::prelude::*;
///
/// fn my_system_function(a_usize_local: Local<usize>) {}
///
/// let system = IntoSystem::into_system(my_system_function);
/// ```
// This trait has to be generic because we have potentially overlapping impls, in particular
// because Rust thinks a type could impl multiple different `FnMut` combinations
// even though none can currently
pub trait IntoSystem<In, Out, Marker>: Sized {
type System: System<In = In, Out = Out>;
/// Turns this value into its corresponding [`System`].
fn into_system(this: Self) -> Self::System;

/// Pass the output of this system `A` into a second system `B`, creating a new compound system.
///
/// The second system must have `In<T>` as its first parameter, where `T`
/// is the return type of the first system.
fn pipe<B, Final, MarkerB>(self, system: B) -> PipeSystem<Self::System, B::System>
where
B: IntoSystem<Out, Final, MarkerB>,
{
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))
}
}

// Systems implicitly implement IntoSystem
impl<In, Out, Sys: System<In = In, Out = Out>> IntoSystem<In, Out, ()> for Sys {
type System = Sys;
fn into_system(this: Self) -> Sys {
this
}
}

/// Wrapper type to mark a [`SystemParam`] as an input.
///
/// [`System`]s may take an optional input which they require to be passed to them when they
/// are being [`run`](System::run). For [`FunctionSystems`](FunctionSystem) the input may be marked
/// with this `In` type, but only the first param of a function may be tagged as an input. This also
/// means a system can only have one or zero input parameters.
///
/// # Examples
///
/// Here is a simple example of a system that takes a [`usize`] returning the square of it.
///
/// ```
/// use bevy_ecs::prelude::*;
///
/// fn main() {
/// let mut square_system = IntoSystem::into_system(square);
///
/// let mut world = World::default();
/// square_system.initialize(&mut world);
/// assert_eq!(square_system.run(12, &mut world), 144);
/// }
///
/// fn square(In(input): In<usize>) -> usize {
/// input * input
/// }
/// ```
pub struct In<In>(pub In);

/// The [`System`] counter part of an ordinary function.
///
/// You get this by calling [`IntoSystem::into_system`] on a function that only accepts
Expand Down
Loading

0 comments on commit ce252f8

Please sign in to comment.