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},
26    scene::graph::GraphError,
27};
28use std::{
29    backtrace::Backtrace,
30    fmt::{Debug, Display, Formatter},
31    sync::atomic::{AtomicBool, Ordering},
32};
33
34static CAPTURE_BACKTRACE: AtomicBool = AtomicBool::new(false);
35
36/// Enables or disables backtrace capture when an error occurs. Backtrace capture is an expensive
37/// operation, so it is disabled by default.
38pub fn enable_backtrace_capture(capture: bool) {
39    CAPTURE_BACKTRACE.store(capture, Ordering::Relaxed);
40}
41
42/// Returns `true` when the backtrace capture is one, `false` - otherwise.
43pub fn is_capturing_backtrace() -> bool {
44    CAPTURE_BACKTRACE.load(Ordering::Relaxed)
45}
46
47/// All possible errors that may occur during script or plugin methods execution.
48pub enum GameErrorKind {
49    /// A [`GraphError`] has occurred.
50    GraphError(GraphError),
51    /// A [`PoolError`] has occurred.
52    PoolError(PoolError),
53    /// An arbitrary, user-defined error has occurred.
54    UserError(UserError),
55    /// A [`VisitError`] has occurred.
56    VisitError(VisitError),
57    /// A [`LoadError`] has occurred.
58    ResourceLoadError(LoadError),
59    /// A [`DynTypeError`] has occurred.
60    DynTypeError(DynTypeError),
61    /// Arbitrary error message.
62    StringError(String),
63}
64
65/// An error that may occur during game code execution.
66pub struct GameError {
67    /// The actual kind of the error.
68    pub kind: GameErrorKind,
69    /// Optional stack trace of the error. See [`enable_backtrace_capture`] for more info.
70    pub trace: Option<Backtrace>,
71}
72
73impl GameError {
74    /// Creates a new error from the specified kind.
75    pub fn new(kind: GameErrorKind) -> Self {
76        Self {
77            kind,
78            trace: if is_capturing_backtrace() {
79                Some(Backtrace::force_capture())
80            } else {
81                None
82            },
83        }
84    }
85
86    /// A shortcut `GameError::user(value)` for `GameError::UserError(Box::new(value))`.
87    pub fn user(value: impl std::error::Error + 'static) -> Self {
88        Self::new(GameErrorKind::UserError(Box::new(value)))
89    }
90
91    /// A shortcut for [`GameErrorKind::StringError`]
92    pub fn str(value: impl AsRef<str>) -> Self {
93        Self::new(GameErrorKind::StringError(value.as_ref().to_string()))
94    }
95}
96
97impl From<GraphError> for GameError {
98    fn from(value: GraphError) -> Self {
99        Self::new(GameErrorKind::GraphError(value))
100    }
101}
102
103impl From<PoolError> for GameError {
104    fn from(value: PoolError) -> Self {
105        Self::new(GameErrorKind::PoolError(value))
106    }
107}
108
109impl From<UserError> for GameError {
110    fn from(value: UserError) -> Self {
111        Self::new(GameErrorKind::UserError(value))
112    }
113}
114
115impl From<LoadError> for GameError {
116    fn from(value: LoadError) -> Self {
117        Self::new(GameErrorKind::ResourceLoadError(value))
118    }
119}
120
121impl From<VisitError> for GameError {
122    fn from(value: VisitError) -> Self {
123        Self::new(GameErrorKind::VisitError(value))
124    }
125}
126
127impl From<DynTypeError> for GameError {
128    fn from(value: DynTypeError) -> Self {
129        Self::new(GameErrorKind::DynTypeError(value))
130    }
131}
132
133impl std::error::Error for GameError {}
134
135impl Display for GameError {
136    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
137        match self.trace.as_ref() {
138            Some(trace) => {
139                write!(f, "{}\nBacktrace:\n{}", self.kind, trace)
140            }
141            None => {
142                write!(
143                    f,
144                    "{}\nBacktrace is unavailable, call `enable_backtrace_capture(true)` to \
145                 enable backtrace capture. Keep in mind that it may be very slow!",
146                    self.kind
147                )
148            }
149        }
150    }
151}
152
153impl Display for GameErrorKind {
154    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
155        match self {
156            Self::GraphError(err) => Display::fmt(&err, f),
157            Self::PoolError(err) => Display::fmt(&err, f),
158            Self::UserError(err) => Display::fmt(&err, f),
159            Self::ResourceLoadError(err) => Display::fmt(&err, f),
160            Self::VisitError(err) => Display::fmt(&err, f),
161            Self::DynTypeError(err) => Display::fmt(&err, f),
162            Self::StringError(msg) => {
163                write!(f, "{msg}")
164            }
165        }
166    }
167}
168
169impl Debug for GameError {
170    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
171        write!(f, "{}", self)
172    }
173}
174
175/// An alias for [`Result`] that has `()` as `Ok` value, and [`GameError`] as error value.
176pub type GameResult = Result<(), GameError>;
177
178/// An arbitrary, user-defined, boxed error type.
179pub type UserError = Box<dyn std::error::Error>;