bevy_ecs/error/
handler.rs

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