Decorate your bevy system with the sysfail macro attribute to handle failure.
use bevy::prelude::*;
use bevy::utils::Duration;
use thiserror::Error;
#[derive(Error, Debug)]
enum GizmoError {
#[error("A Gizmo error")]
Error,
}
#[derive(Debug, PartialEq, Eq, Hash, SystemSet, Clone)]
enum TransformGizmoSystem { Drag, Place }
fn main() {
let mut app = App::new();
app.add_plugins(bevy::time::TimePlugin)
.add_systems(Update, (
drag_gizmo
.pipe(print_gizmo_error)
.in_set(TransformGizmoSystem::Drag),
delete_gizmo
.pipe(|In(_)| {})
.after(TransformGizmoSystem::Place),
place_gizmo
.pipe(print_gizmo_error)
.in_set(TransformGizmoSystem::Place)
.after(TransformGizmoSystem::Drag),
));
app.update();
}
fn print_gizmo_error(
In(result): In<Result<(), Box<dyn std::error::Error>>>,
mut last_error_occurence: Local<Option<Duration>>,
time: Res<Time>,
) {
// error boilerplate, may include
// - avoiding printing multiple times the same error
// - Formatting and chosing the log level
}
fn drag_gizmo(time: Res<Time>) -> Result<(), Box<dyn std::error::Error>> {
println!("drag time is: {}", time.elapsed_seconds());
let _ = Err(GizmoError::Error)?;
println!("This will never print");
Ok(())
}
fn place_gizmo() -> Result<(), Box<dyn std::error::Error>> {
let () = Result::<(), &'static str>::Ok(())?;
println!("this line should actually show up");
let _ = Err("Ah, some creative use of info logging I see")?;
Ok(())
}
fn delete_gizmo(time: Res<Time>) -> Option<()> {
println!("delete time is: {}", time.elapsed_seconds());
let _ = None?;
println!("This will never print");
Some(())
}use bevy::prelude::*;
use bevy_mod_sysfail::prelude::*;
use thiserror::Error;
#[derive(Error, Debug)]
enum GizmoError {
#[error("A Gizmo error")]
Error,
}
fn main() {
let mut app = App::new();
app.add_plugins(bevy::time::TimePlugin)
.add_systems(Update, (
drag_gizmo,
delete_gizmo.after(place_gizmo),
place_gizmo.after(drag_gizmo)
));
app.update();
}
#[sysfail]
fn drag_gizmo(time: Res<Time>) {
println!("drag time is: {}", time.elapsed_seconds());
let _ = Err(GizmoError::Error)?;
println!("This will never print");
}
#[sysfail(Log<&'static str, Info>)]
fn place_gizmo() {
let () = Result::<(), &'static str>::Ok(())?;
println!("this line should actually show up");
let _ = Err("Ah, some creative use of info logging I see")?;
}
#[sysfail(Ignore)]
fn delete_gizmo(time: Res<Time>) {
println!("delete time is: {}", time.elapsed_seconds());
let _ = Err(342_i32)?;
println!("This will never print");
}sysfail is an attribute macro you can slap on top of your systems to define
the handling of errors. Unlike pipe, this is done directly at the definition
site, and not when adding to the app. As a result, it's easy to see at a glance
what kind of error handling is happening in the system, it also allows using
the system name as a label in system dependency specification.
sysfail(E) systems return a value of type Result<(), E>. The return type
is added by the macro, so do not add it yourself!
E is a type that implements the Failure trait. bevy_mod_sysfail exports
several types that implement Failure:
Log<Err, Lvl = Warn>: Will logErrto the tracing logger.- The first type parameter
Errimplements theDeduptrait. You can implementDedupfor your own types, but you can always use theanyhow::Error,Box<dyn std::error::Error>and&'static strtypes, as those already implementDedup. - The second type parameter specifies the level of the log. It is optional
and by default it is
Warn
- The first type parameter
LogSimply: Is similar toLog, but without deduplication.Emit<Ev>: Will emit theEvbevyEventwhenever the system returns anErrIgnore: Ignore errors, do as if nothing happened.
Example usages:
use bevy::prelude::*;
use bevy_mod_sysfail::prelude::*;
use thiserror::Error;
// -- Log a value --
#[derive(Error, Debug)]
enum MyCustomError {
#[error("A Custom error")]
Error,
}
// Equivalent to #[sysfail(Log<Box<dyn std::error::Error>>)]
#[sysfail]
fn generic_failure() { /* ... */ }
#[sysfail(Log<&'static str>)]
fn log_a_str_message() {
let _ = Err("Yep, just like that")?;
}
#[sysfail(Log<anyhow::Error>)]
fn log_an_anyhow_error() {
let _ = Err(MyCustomError::Error)?;
}
#[sysfail(LogSimply<MyCustomError, Trace>)]
fn log_trace_on_failure() { /* ... */ }
#[sysfail(LogSimply<MyCustomError, Error>)]
fn log_error_on_failure() { /* ... */ }
// -- Emit an event --
use bevy::app::AppExit;
#[derive(Event)]
enum ChangeMenu {
Main,
Tools,
}
#[sysfail(Emit<ChangeMenu>)]
fn change_menu() { /* ... */ }
#[sysfail(Emit<AppExit>)]
fn quit_app_on_error() { /* ... */ }
// -- Ignore all errors --
#[sysfail(Ignore)]
fn do_not_care_about_failure() { /* ... */ }For exclusive systems, use the #[exclusive_sysfail] macro. Note that only
Failure<Param = ()> work with exclusive systems. This excludes Log, so
make sure to use LogSimply instead.
bevy_mod_sysfail is not limited to the predefined set of Failures, you can
define your own by implementing it yourself.
See the custom_failure example for sample code.
See the CHANGELOG.
| bevy | latest supporting version |
|---|---|
| 0.13 | 7.0.0 |
| 0.12 | 6.0.0 |
| 0.11 | 4.3.0 |
| 0.10 | 2.0.0 |
| 0.9 | 1.1.0 |
| 0.8 | 0.1.0 |
Copyright © 2022 Nicola Papale
This software is licensed under Apache 2.0.