use crate::{
asset::state::LoadError,
core::{dyntype::DynTypeError, pool::PoolError, visitor::error::VisitError, warn},
graphics::error::FrameworkError,
scene::graph::GraphError,
};
use std::{
backtrace::Backtrace,
fmt::{Debug, Display, Formatter},
sync::atomic::{AtomicBool, Ordering},
};
static CAPTURE_BACKTRACE: AtomicBool = AtomicBool::new(false);
pub fn enable_backtrace_capture(capture: bool) {
CAPTURE_BACKTRACE.store(capture, Ordering::Relaxed);
if capture {
warn!(
"Backtrace capture is enabled! This will negatively impact performance in \
case of error spam."
)
}
}
pub fn is_capturing_backtrace() -> bool {
CAPTURE_BACKTRACE.load(Ordering::Relaxed)
}
pub enum GameErrorKind {
GraphError(GraphError),
PoolError(PoolError),
UserError(UserError),
VisitError(VisitError),
ResourceLoadError(LoadError),
DynTypeError(DynTypeError),
StringError(String),
FrameworkError(FrameworkError),
}
pub struct GameError {
pub kind: GameErrorKind,
pub trace: Option<Backtrace>,
}
impl GameError {
pub fn new(kind: GameErrorKind) -> Self {
Self {
kind,
trace: if is_capturing_backtrace() {
Some(Backtrace::force_capture())
} else {
None
},
}
}
pub fn user(value: impl std::error::Error + Send + 'static) -> Self {
Self::new(GameErrorKind::UserError(Box::new(value)))
}
pub fn str(value: impl AsRef<str>) -> Self {
Self::new(GameErrorKind::StringError(value.as_ref().to_string()))
}
}
impl From<GraphError> for GameError {
fn from(value: GraphError) -> Self {
Self::new(GameErrorKind::GraphError(value))
}
}
impl From<PoolError> for GameError {
fn from(value: PoolError) -> Self {
Self::new(GameErrorKind::PoolError(value))
}
}
impl From<UserError> for GameError {
fn from(value: UserError) -> Self {
Self::new(GameErrorKind::UserError(value))
}
}
impl From<LoadError> for GameError {
fn from(value: LoadError) -> Self {
Self::new(GameErrorKind::ResourceLoadError(value))
}
}
impl From<VisitError> for GameError {
fn from(value: VisitError) -> Self {
Self::new(GameErrorKind::VisitError(value))
}
}
impl From<DynTypeError> for GameError {
fn from(value: DynTypeError) -> Self {
Self::new(GameErrorKind::DynTypeError(value))
}
}
impl From<FrameworkError> for GameError {
fn from(value: FrameworkError) -> Self {
Self::new(GameErrorKind::FrameworkError(value))
}
}
impl std::error::Error for GameError {}
impl Display for GameError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.trace.as_ref() {
Some(trace) => {
write!(f, "{}\nBacktrace:\n{}", self.kind, trace)
}
None => {
write!(
f,
"{}\nBacktrace is unavailable, call `enable_backtrace_capture(true)` to \
enable backtrace capture. Keep in mind that it may be very slow!",
self.kind
)
}
}
}
}
impl Display for GameErrorKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::GraphError(err) => Display::fmt(&err, f),
Self::PoolError(err) => Display::fmt(&err, f),
Self::UserError(err) => Display::fmt(&err, f),
Self::ResourceLoadError(err) => Display::fmt(&err, f),
Self::VisitError(err) => Display::fmt(&err, f),
Self::DynTypeError(err) => Display::fmt(&err, f),
Self::StringError(msg) => {
write!(f, "{msg}")
}
Self::FrameworkError(err) => Display::fmt(&err, f),
}
}
}
impl Debug for GameError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self)
}
}
pub type GameResult = Result<(), GameError>;
pub type UserError = Box<dyn std::error::Error + Send>;