use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use serde::Deserialize;
use serde::Serialize;
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
archive(
check_bytes,
bound(serialize = "__S: rkyv::ser::ScratchSpace + rkyv::ser::Serializer")
),
archive_attr(check_bytes(
bound = "__C: rkyv::validation::ArchiveContext, <__C as rkyv::Fallible>::Error: std::error::Error"
),)
)]
pub struct AnyError {
typ: Option<String>,
msg: String,
#[cfg_attr(feature = "rkyv", omit_bounds, archive_attr(omit_bounds))]
source: Option<Box<AnyError>>,
context: Vec<String>,
backtrace: Option<String>,
}
impl Display for AnyError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
if let Some(t) = &self.typ {
write!(f, "{}: ", t)?;
}
write!(f, "{}", self.msg)?;
for (i, ctx) in self.context.iter().enumerate() {
if f.alternate() {
writeln!(f)?;
write!(f, " while: {}", ctx)?;
} else {
if i > 0 {
write!(f, ",")?;
}
write!(f, " while: {}", ctx)?;
}
}
#[allow(clippy::collapsible_else_if)]
if f.alternate() {
if let Some(ref s) = self.source {
writeln!(f)?;
write!(f, "source: ")?;
write!(f, "{:#}", s)?;
}
} else {
if let Some(ref s) = self.source {
write!(f, "; source: {}", s)?;
}
}
Ok(())
}
}
impl std::fmt::Debug for AnyError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
<Self as Display>::fmt(self, f)?;
if let Some(ref b) = self.backtrace {
write!(f, "\nbacktrace:\n{}", b)?;
}
Ok(())
}
}
impl std::error::Error for AnyError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match &self.source {
Some(x) => Some(x.as_ref()),
None => None,
}
}
}
#[cfg(feature = "anyhow")]
impl From<anyhow::Error> for AnyError {
fn from(a: anyhow::Error) -> Self {
let source = a.source().map(|x| Box::new(AnyError::from_dyn(x, None)));
#[cfg(feature = "backtrace")]
let bt = Some({
let bt = a.backtrace();
format!("{:?}", bt)
});
#[cfg(not(feature = "backtrace"))]
let bt = None;
Self {
typ: None,
msg: a.to_string(),
source,
context: vec![],
backtrace: bt,
}
}
}
impl<E> From<&E> for AnyError
where E: std::error::Error + 'static
{
fn from(e: &E) -> Self {
AnyError::new(e)
}
}
impl AnyError {
pub fn error(msg: impl ToString) -> Self {
Self {
typ: None,
msg: msg.to_string(),
source: None,
context: vec![],
backtrace: None,
}
}
pub fn new<E>(e: &E) -> Self
where E: Error + 'static {
let q: &(dyn Error + 'static) = e;
let x = q.downcast_ref::<AnyError>();
let typ = match x {
Some(ae) => ae.typ.clone(),
None => Some(std::any::type_name::<E>().to_string()),
};
Self::from_dyn(e, typ)
}
pub fn from_dyn(e: &(dyn Error + 'static), typ: Option<String>) -> Self {
let x = e.downcast_ref::<AnyError>();
return match x {
Some(ae) => ae.clone(),
None => {
#[cfg(feature = "backtrace")]
let bt = crate::bt::error_backtrace_str(e);
#[cfg(not(feature = "backtrace"))]
let bt = None;
let source = e.source().map(|x| Box::new(AnyError::from_dyn(x, None)));
Self {
typ,
msg: e.to_string(),
source,
context: vec![],
backtrace: bt,
}
}
};
}
#[cfg(feature = "backtrace")]
#[must_use]
pub fn with_backtrace(mut self) -> Self {
if self.backtrace.is_some() {
return self;
}
self.backtrace = Some(crate::bt::new_str());
self
}
#[must_use]
pub fn add_context<D: Display, F: FnOnce() -> D>(mut self, ctx: F) -> Self {
self.context.push(format!("{}", ctx()));
self
}
pub fn get_type(&self) -> Option<&str> {
self.typ.as_ref().map(|x| x as _)
}
pub fn backtrace(&self) -> Option<&str> {
self.backtrace.as_ref().map(|x| x as _)
}
}
pub fn backtrace_str() -> Option<String> {
#[cfg(feature = "backtrace")]
return Some(crate::bt::new_str());
#[cfg(not(feature = "backtrace"))]
return None;
}