Crate bevy_mod_sysfail
source ·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::macros::*;
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(log)]
fn drag_gizmo(time: Res<Time>) -> Result<(), anyhow::Error> {
println!("drag time is: {}", time.elapsed_seconds());
let _ = Err(GizmoError::Error)?;
println!("This will never print");
Ok(())
}
#[sysfail(log(level = "info"))]
fn place_gizmo() -> Result<(), &'static str> {
let () = Result::<(), &'static str>::Ok(())?;
println!("this line should actually show up");
let _ = Err("Ah, some creative use of info logging I see")?;
Ok(())
}
#[quick_sysfail]
fn delete_gizmo(time: Res<Time>) {
println!("delete time is: {}", time.elapsed_seconds());
let _ = None?;
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.
The sysfail
attribute can only be used on systems returning a type
implementing the Failure
trait. Failure
is implemented for
Result<(), impl FailureMode>
and Option<()>
.
sysfail
takes a single argument, it is one of the following:
log
: print theErr
of theResult
return value, prints a very generic “A none value” when the return type isOption
. By default, most things are logged aterror
level.log(level = "{silent,trace,debug,info,warn,error}")
: This forces logging of errors at a certain level (make sure to add the quotes)ignore
: This is a shortcut forlog(level = "silent")
Note that when the level is not "silent"
, bevy_mod_sysfail
adds the
Res<Time>
and Local<LoggedErrors>
system parameters to be able to supress
repeating error messages.
quick_sysfail
attribute
quick_sysfail
is like sysfail(ignore)
but only works on Option<()>
.
This attribute, unlike sysfail
allows you to elide the final Some(())
and the type signature of the system. It’s for the maximally lazy, like
me.
use bevy_mod_sysfail::macros::*;
#[sysfail(ignore)]
fn place_gizmo() -> Option<()> {
// …
Some(())
}
// equivalent to:
#[quick_sysfail]
fn quick_place_gizmo() {
// …
}
Traits
How error is handled is not very customizable, but there is a few behaviors controllable by the user, always through traits.
Failure
trait
Failure
is implemented for Result<(), impl FailureMode>
and Option<()>
.
Systems marked with the sysfail
attribute must return a type implementing
Failure
.
FailureMode
trait
FailureMode
defines how the failure is handled. By implementing the
trait on your own error types, you can specify:
- What constitutes “distinct” error types.
- How long an error must not be produced in order to be displayed again.
FailureMode
is implemented for Box<dyn Error>
, anyhow::Error
, ()
and &'static str
.
Change log
See CHANGELOG.md
Version Matrix
bevy | latest supporting version |
---|---|
0.12 | 5.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
- macrosDeprecatedThe
quick_sysfail
andsysfail
macros. - This crate’s traits.
Structs
- Tracks when specific errors were logged.
- OverrideLevelDeprecatedDeprecated,
bevy_mod_sysfail
doesn’t respect the runtime-set log level.
Enums
- LogLevelDeprecatedDeprecated: It is now completely unused.
Traits
- Something that can be returned by a function marked with
#[sysfail(log)]
. - Something that can be logged in a
sysfail
handler - LogLevelOverrideDeprecatedDeprecated: This is completely ignored by
bevy_mod_sysfail
.
Attribute Macros
sysfail
is an attribute macro you can slap on top of your systems to define the handling of errors.