Expand description
§Bevy system error handling
Decorate your bevy system with the sysfail macro attribute to handle failure.
§Before
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(())
}§After
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 attribute
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() { /* ... */ }§Exclusive systems
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.
§Custom handling
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.
§Change log
See the CHANGELOG.
§Version Matrix
| 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 |
§License
Copyright © 2022 Nicola Papale
This software is licensed under Apache 2.0.
Modules§
- prelude
- Useful set of
Failuredefault implementations andLogLevelModifiers.
Structs§
- Level
- Describes the level of verbosity of a span or event.
Traits§
- Callsite
- Trait implemented by callsites.
- Dedup
- An error type with a cooldown and a category.
- Failure
- The
Errside of the return type of#[sysfail]. - LogLevel
Modifier - Modifier for the
LogandLogSimplyFailures.
Attribute Macros§
- exclusive_
sysfail - See the
crate-level documentation for usage and examples. Similar tosysfailbut allows usage on exclusive systems. - sysfail
- See the
crate-level documentation for usage and examples.sysfailis an attribute macro you can slap on top of your systems to define the handling of errors.