use std::any::Any;
use std::borrow::Cow;
use std::error::Error as StdError;
use std::fmt::{Debug, Display};
use std::panic::Location;
use crate::ext::ErrorExt;
pub trait WithContext: StdError + Any {
fn with_context(self, context: Frame) -> Self;
fn location(&self) -> Option<&'static Location<'static>>;
}
pub struct Frame {
pub source: Box<dyn StdError + Send + Sync + 'static>,
pub location: &'static Location<'static>,
}
impl<T> From<T> for Frame
where
T: Into<Box<dyn StdError + Send + Sync + 'static>>,
{
#[track_caller]
fn from(value: T) -> Self {
let source = value.into();
let location = Location::caller();
Frame { source, location }
}
}
pub struct Error {
pub message: Cow<'static, str>,
pub source: Option<Box<dyn StdError + Send + Sync + 'static>>,
pub location: Option<&'static Location<'static>>,
}
impl Error {
pub fn new(msg: impl Into<Cow<'static, str>>) -> Error {
Error {
message: msg.into(),
source: None,
location: None,
}
}
}
impl Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.report().fmt(f)
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(loc) = self.location {
write!(f, "{}, at {}", self.message, loc)
} else {
f.write_str(&self.message)
}
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.source.as_deref().map(|s| s as _)
}
}
impl WithContext for Error {
fn with_context(mut self, context: Frame) -> Self {
self.source = Some(context.source);
self.location = Some(context.location);
self
}
fn location(&self) -> Option<&'static Location<'static>> {
self.location
}
}
impl From<(Cow<'static, str>, Frame)> for Error {
fn from(value: (Cow<'static, str>, Frame)) -> Self {
Error {
message: value.0,
source: Some(value.1.source),
location: Some(value.1.location),
}
}
}