Skip to main content

fyrox_impl/plugin/
error.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Contains all possible errors that may occur during script or plugin methods execution.
22
23use crate::{
24    asset::state::LoadError,
25    core::{dyntype::DynTypeError, pool::PoolError, visitor::error::VisitError, warn},
26    graphics::error::FrameworkError,
27    scene::graph::GraphError,
28};
29use std::{
30    backtrace::Backtrace,
31    fmt::{Debug, Display, Formatter},
32    sync::atomic::{AtomicBool, Ordering},
33};
34
35static CAPTURE_BACKTRACE: AtomicBool = AtomicBool::new(false);
36
37/// Enables or disables backtrace capture when an error occurs. Backtrace capture is an expensive
38/// operation, so it is disabled by default.
39pub fn enable_backtrace_capture(capture: bool) {
40    CAPTURE_BACKTRACE.store(capture, Ordering::Relaxed);
41
42    if capture {
43        warn!(
44            "Backtrace capture is enabled! This will negatively impact performance in \
45        case of error spam."
46        )
47    }
48}
49
50/// Returns `true` when the backtrace capture is one, `false` - otherwise.
51pub fn is_capturing_backtrace() -> bool {
52    CAPTURE_BACKTRACE.load(Ordering::Relaxed)
53}
54
55/// All possible errors that may occur during script or plugin methods execution.
56pub enum GameErrorKind {
57    /// A [`GraphError`] has occurred.
58    GraphError(GraphError),
59    /// A [`PoolError`] has occurred.
60    PoolError(PoolError),
61    /// An arbitrary, user-defined error has occurred.
62    UserError(UserError),
63    /// A [`VisitError`] has occurred.
64    VisitError(VisitError),
65    /// A [`LoadError`] has occurred.
66    ResourceLoadError(LoadError),
67    /// A [`DynTypeError`] has occurred.
68    DynTypeError(DynTypeError),
69    /// Arbitrary error message.
70    StringError(String),
71    /// A [`FrameworkError`] has occurred.
72    FrameworkError(FrameworkError),
73}
74
75/// An error that may occur during game code execution.
76pub struct GameError {
77    /// The actual kind of the error.
78    pub kind: GameErrorKind,
79    /// Optional stack trace of the error. See [`enable_backtrace_capture`] for more info.
80    pub trace: Option<Backtrace>,
81}
82
83impl GameError {
84    /// Creates a new error from the specified kind.
85    pub fn new(kind: GameErrorKind) -> Self {
86        Self {
87            kind,
88            trace: if is_capturing_backtrace() {
89                Some(Backtrace::force_capture())
90            } else {
91                None
92            },
93        }
94    }
95
96    /// A shortcut `GameError::user(value)` for `GameError::UserError(Box::new(value))`.
97    pub fn user(value: impl std::error::Error + Send + 'static) -> Self {
98        Self::new(GameErrorKind::UserError(Box::new(value)))
99    }
100
101    /// A shortcut for [`GameErrorKind::StringError`]
102    pub fn str(value: impl AsRef<str>) -> Self {
103        Self::new(GameErrorKind::StringError(value.as_ref().to_string()))
104    }
105}
106
107impl From<GraphError> for GameError {
108    fn from(value: GraphError) -> Self {
109        Self::new(GameErrorKind::GraphError(value))
110    }
111}
112
113impl From<PoolError> for GameError {
114    fn from(value: PoolError) -> Self {
115        Self::new(GameErrorKind::PoolError(value))
116    }
117}
118
119impl From<UserError> for GameError {
120    fn from(value: UserError) -> Self {
121        Self::new(GameErrorKind::UserError(value))
122    }
123}
124
125impl From<LoadError> for GameError {
126    fn from(value: LoadError) -> Self {
127        Self::new(GameErrorKind::ResourceLoadError(value))
128    }
129}
130
131impl From<VisitError> for GameError {
132    fn from(value: VisitError) -> Self {
133        Self::new(GameErrorKind::VisitError(value))
134    }
135}
136
137impl From<DynTypeError> for GameError {
138    fn from(value: DynTypeError) -> Self {
139        Self::new(GameErrorKind::DynTypeError(value))
140    }
141}
142
143impl From<FrameworkError> for GameError {
144    fn from(value: FrameworkError) -> Self {
145        Self::new(GameErrorKind::FrameworkError(value))
146    }
147}
148
149impl std::error::Error for GameError {}
150
151impl Display for GameError {
152    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
153        match self.trace.as_ref() {
154            Some(trace) => {
155                write!(f, "{}\nBacktrace:\n{}", self.kind, trace)
156            }
157            None => {
158                write!(
159                    f,
160                    "{}\nBacktrace is unavailable, call `enable_backtrace_capture(true)` to \
161                 enable backtrace capture. Keep in mind that it may be very slow!",
162                    self.kind
163                )
164            }
165        }
166    }
167}
168
169impl Display for GameErrorKind {
170    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
171        match self {
172            Self::GraphError(err) => Display::fmt(&err, f),
173            Self::PoolError(err) => Display::fmt(&err, f),
174            Self::UserError(err) => Display::fmt(&err, f),
175            Self::ResourceLoadError(err) => Display::fmt(&err, f),
176            Self::VisitError(err) => Display::fmt(&err, f),
177            Self::DynTypeError(err) => Display::fmt(&err, f),
178            Self::StringError(msg) => {
179                write!(f, "{msg}")
180            }
181            Self::FrameworkError(err) => Display::fmt(&err, f),
182        }
183    }
184}
185
186impl Debug for GameError {
187    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
188        write!(f, "{}", self)
189    }
190}
191
192/// An alias for [`Result`] that has `()` as `Ok` value, and [`GameError`] as error value.
193pub type GameResult = Result<(), GameError>;
194
195/// An arbitrary, user-defined, boxed error type.
196pub type UserError = Box<dyn std::error::Error + Send>;