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] - [Fixes #6224] Add logging variants of system piping #6751

Closed
7 changes: 4 additions & 3 deletions crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ pub mod prelude {
Schedule, Stage, StageLabel, State, SystemLabel, SystemSet, SystemStage,
},
system::{
adapter as system_adapter, Commands, In, IntoPipeSystem, IntoSystem, Local, NonSend,
NonSendMut, ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut,
Resource, System, SystemParamFunction,
adapter as system_adapter,
adapter::{dbg, error, ignore, info, unwrap, warn},
Commands, In, IntoPipeSystem, IntoSystem, Local, NonSend, NonSendMut, ParallelCommands,
ParamSet, Query, RemovedComponents, Res, ResMut, Resource, System, SystemParamFunction,
},
world::{FromWorld, Mut, World},
};
Expand Down
137 changes: 137 additions & 0 deletions crates/bevy_ecs/src/system/system_piping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ where
/// A collection of common adapters for [piping](super::PipeSystem) the result of a system.
pub mod adapter {
use crate::system::In;
use bevy_utils::tracing;
use std::fmt::Debug;

/// Converts a regular function into a system adapter.
Expand Down Expand Up @@ -232,6 +233,142 @@ pub mod adapter {
res.unwrap()
}

/// System adapter that utilizes the [`bevy_utils::tracing::info!`] macro to print system information.
///
/// # Examples
///
/// ```
/// use bevy_ecs::prelude::*;
/// #
/// # #[derive(StageLabel)]
/// # enum CoreStage { Update };
///
/// // Building a new schedule/app...
/// # use bevy_ecs::schedule::SystemStage;
/// # let mut sched = Schedule::default(); sched
/// # .add_stage(CoreStage::Update, SystemStage::parallel())
/// .add_system_to_stage(
/// CoreStage::Update,
/// // Prints system information.
/// data_pipe_system.pipe(system_adapter::info)
/// )
/// // ...
/// # ;
/// # let mut world = World::new();
/// # sched.run(&mut world);
///
/// // A system that returns a String output.
/// fn data_pipe_system() -> String {
/// "42".to_string()
/// }
/// ```
pub fn info<T: Debug>(In(data): In<T>) {
tracing::info!("{:?}", data);
}

/// System adapter that utilizes the [`bevy_utils::tracing::debug!`] macro to print the output of a system.
///
/// # Examples
///
/// ```
/// use bevy_ecs::prelude::*;
/// #
/// # #[derive(StageLabel)]
/// # enum CoreStage { Update };
///
/// // Building a new schedule/app...
/// # use bevy_ecs::schedule::SystemStage;
/// # let mut sched = Schedule::default(); sched
/// # .add_stage(CoreStage::Update, SystemStage::parallel())
/// .add_system_to_stage(
/// CoreStage::Update,
/// // Prints debug data from system.
/// parse_message_system.pipe(system_adapter::dbg)
/// )
/// // ...
/// # ;
/// # let mut world = World::new();
/// # sched.run(&mut world);
///
/// // A system that returns a Result<usize, String> output.
/// fn parse_message_system() -> Result<usize, std::num::ParseIntError> {
/// Ok("42".parse()?)
/// }
/// ```
pub fn dbg<T: Debug>(In(data): In<T>) {
tracing::debug!("{:?}", data);
}

/// System adapter that utilizes the [`bevy_utils::tracing::warn!`] macro to print the output of a system.
///
/// # Examples
///
/// ```
/// use bevy_ecs::prelude::*;
/// #
/// # #[derive(StageLabel)]
/// # enum CoreStage { Update };
///
/// // Building a new schedule/app...
/// # use bevy_ecs::schedule::SystemStage;
/// # let mut sched = Schedule::default(); sched
/// # .add_stage(CoreStage::Update, SystemStage::parallel())
/// .add_system_to_stage(
/// CoreStage::Update,
/// // Prints system warning if system returns an error.
/// warning_pipe_system.pipe(system_adapter::warn)
/// )
/// // ...
/// # ;
/// # let mut world = World::new();
/// # sched.run(&mut world);
///
/// // A system that returns a Result<(), String> output.
/// fn warning_pipe_system() -> Result<(), String> {
/// Err("Got to rusty?".to_string())
/// }
/// ```
pub fn warn<E: Debug>(In(res): In<Result<(), E>>) {
if let Err(warn) = res {
tracing::warn!("{:?}", warn);
}
}

/// System adapter that utilizes the [`bevy_utils::tracing::error!`] macro to print the output of a system.
///
/// # Examples
///
/// ```
/// use bevy_ecs::prelude::*;
/// #
/// # #[derive(StageLabel)]
/// # enum CoreStage { Update };
///
/// // Building a new schedule/app...
/// # use bevy_ecs::schedule::SystemStage;
/// # let mut sched = Schedule::default(); sched
/// # .add_stage(CoreStage::Update, SystemStage::parallel())
/// .add_system_to_stage(
/// CoreStage::Update,
/// // Prints system error if system fails.
/// parse_error_message_system.pipe(system_adapter::error)
/// )
/// // ...
/// # ;
/// # let mut world = World::new();
/// # sched.run(&mut world);
///
/// // A system that returns a Result<())> output.
/// fn parse_error_message_system() -> Result<(), String> {
/// Err("Some error".to_owned())
/// }
/// ```
pub fn error<E: Debug>(In(res): In<Result<(), E>>) {
if let Err(error) = res {
tracing::error!("{:?}", error);
}
}

/// 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`.
///
Expand Down
36 changes: 35 additions & 1 deletion examples/ecs/system_piping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,43 @@
use anyhow::Result;
use bevy::prelude::*;

use bevy::log::LogPlugin;
use bevy::utils::tracing::Level;

fn main() {
App::new()
.insert_resource(Message("42".to_string()))
.insert_resource(OptionalWarning(Err("Got to rusty?".to_string())))
.add_plugin(LogPlugin {
level: Level::TRACE,
filter: "".to_string(),
})
.add_system(parse_message_system.pipe(handler_system))
.add_system(data_pipe_system.pipe(info))
.add_system(parse_message_system.pipe(dbg))
.add_system(warning_pipe_system.pipe(warn))
.add_system(parse_error_message_system.pipe(error))
.add_system(parse_message_system.pipe(ignore))
.run();
}

#[derive(Resource, Deref)]
struct Message(String);

// this system produces a Result<usize> output by trying to parse the Message resource
#[derive(Resource, Deref)]
struct OptionalWarning(Result<(), String>);

// This system produces a Result<usize> output by trying to parse the Message resource.
fn parse_message_system(message: Res<Message>) -> Result<usize> {
Ok(message.parse::<usize>()?)
}

// This system produces a Result<()> output by trying to parse the Message resource.
fn parse_error_message_system(message: Res<Message>) -> Result<()> {
message.parse::<usize>()?;
Ok(())
}

// This system takes a Result<usize> input and either prints the parsed value or the error message
// Try changing the Message resource to something that isn't an integer. You should see the error
// message printed.
Expand All @@ -28,3 +50,15 @@ fn handler_system(In(result): In<Result<usize>>) {
Err(err) => println!("encountered an error: {err:?}"),
}
}

// This system produces a String output by trying to clone the String from the Message resource.
fn data_pipe_system(message: Res<Message>) -> String {
message.0.clone()
}

// This system produces an Result<String> output by trying to extract a String from the
// OptionalWarning resource. Try changing the OptionalWarning resource to None. You should
// not see the warning message printed.
fn warning_pipe_system(message: Res<OptionalWarning>) -> Result<(), String> {
message.0.clone()
}