Skip to main content

bevy_ecs/error/
handler.rs

1use core::fmt::Display;
2
3use crate::{change_detection::Tick, error::BevyError, prelude::Resource};
4use bevy_ecs::error::Severity;
5use bevy_utils::prelude::DebugName;
6use derive_more::derive::{Deref, DerefMut};
7
8/// Context for a [`BevyError`] to aid in debugging.
9#[derive(Debug, PartialEq, Eq, Clone)]
10pub enum ErrorContext {
11    /// The error occurred in a system.
12    System {
13        /// The name of the system that failed.
14        name: DebugName,
15        /// The last tick that the system was run.
16        last_run: Tick,
17    },
18    /// The error occurred in a run condition.
19    RunCondition {
20        /// The name of the run condition that failed.
21        name: DebugName,
22        /// The last tick that the run condition was evaluated.
23        last_run: Tick,
24        /// The system this run condition is attached to.
25        system: DebugName,
26        /// `true` if this run condition was on a set.
27        on_set: bool,
28    },
29    /// The error occurred in a command.
30    Command {
31        /// The name of the command that failed.
32        name: DebugName,
33    },
34    /// The error occurred in an observer.
35    Observer {
36        /// The name of the observer that failed.
37        name: DebugName,
38        /// The last tick that the observer was run.
39        last_run: Tick,
40    },
41}
42
43impl Display for ErrorContext {
44    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45        match self {
46            Self::System { name, .. } => {
47                write!(f, "System `{name}` failed")
48            }
49            Self::Command { name } => write!(f, "Command `{name}` failed"),
50            Self::Observer { name, .. } => {
51                write!(f, "Observer `{name}` failed")
52            }
53            Self::RunCondition {
54                name,
55                system,
56                on_set,
57                ..
58            } => {
59                write!(
60                    f,
61                    "Run condition `{name}` failed for{} system `{system}`",
62                    if *on_set { " set containing" } else { "" }
63                )
64            }
65        }
66    }
67}
68
69impl ErrorContext {
70    /// The name of the ECS construct that failed.
71    pub fn name(&self) -> DebugName {
72        match self {
73            Self::System { name, .. }
74            | Self::Command { name, .. }
75            | Self::Observer { name, .. }
76            | Self::RunCondition { name, .. } => name.clone(),
77        }
78    }
79
80    /// A string representation of the kind of ECS construct that failed.
81    ///
82    /// This is a simpler helper used for logging.
83    pub fn kind(&self) -> &str {
84        match self {
85            Self::System { .. } => "system",
86            Self::Command { .. } => "command",
87            Self::Observer { .. } => "observer",
88            Self::RunCondition { .. } => "run condition",
89        }
90    }
91}
92
93macro_rules! inner {
94    ($call:path, $e:ident, $c:ident) => {
95        $call!(
96            "Encountered an error in {} `{}`: {}",
97            $c.kind(),
98            $c.name(),
99            $e
100        );
101    };
102}
103
104/// Defines how Bevy reacts to errors.
105pub type ErrorHandler = fn(BevyError, ErrorContext);
106
107/// Fallback error handler to call when an error is not handled otherwise.
108/// Defaults to [`match_severity()`].
109///
110/// When updated while a [`Schedule`] is running, it doesn't take effect for
111/// that schedule until it's completed.
112///
113/// [`Schedule`]: crate::schedule::Schedule
114#[derive(Resource, Deref, DerefMut, Copy, Clone)]
115pub struct FallbackErrorHandler(pub ErrorHandler);
116
117impl Default for FallbackErrorHandler {
118    fn default() -> Self {
119        Self(match_severity)
120    }
121}
122
123/// Deprecated alias for [`FallbackErrorHandler`].
124#[deprecated(since = "0.19.0", note = "Renamed to `FallbackErrorHandler`.")]
125pub type DefaultErrorHandler = FallbackErrorHandler;
126
127/// Error handler that defers to an error's [`Severity`].
128#[track_caller]
129#[inline]
130pub fn match_severity(err: BevyError, ctx: ErrorContext) {
131    match err.severity() {
132        Severity::Ignore => ignore(err, ctx),
133        Severity::Trace => trace(err, ctx),
134        Severity::Debug => debug(err, ctx),
135        Severity::Info => info(err, ctx),
136        Severity::Warning => warn(err, ctx),
137        Severity::Error => error(err, ctx),
138        Severity::Panic => panic(err, ctx),
139    }
140}
141
142/// Error handler that panics with the system error.
143#[track_caller]
144#[inline]
145pub fn panic(error: BevyError, ctx: ErrorContext) {
146    inner!(panic, error, ctx);
147}
148
149/// Error handler that logs the system error at the `error` level.
150#[track_caller]
151#[inline]
152pub fn error(error: BevyError, ctx: ErrorContext) {
153    inner!(log::error, error, ctx);
154}
155
156/// Error handler that logs the system error at the `warn` level.
157#[track_caller]
158#[inline]
159pub fn warn(error: BevyError, ctx: ErrorContext) {
160    inner!(log::warn, error, ctx);
161}
162
163/// Error handler that logs the system error at the `info` level.
164#[track_caller]
165#[inline]
166pub fn info(error: BevyError, ctx: ErrorContext) {
167    inner!(log::info, error, ctx);
168}
169
170/// Error handler that logs the system error at the `debug` level.
171#[track_caller]
172#[inline]
173pub fn debug(error: BevyError, ctx: ErrorContext) {
174    inner!(log::debug, error, ctx);
175}
176
177/// Error handler that logs the system error at the `trace` level.
178#[track_caller]
179#[inline]
180pub fn trace(error: BevyError, ctx: ErrorContext) {
181    inner!(log::trace, error, ctx);
182}
183
184/// Error handler that ignores the system error.
185#[track_caller]
186#[inline]
187pub fn ignore(_: BevyError, _: ErrorContext) {}