use crate::error::Error;
use serde::{Serialize, Serializer};
use std::{
backtrace::{Backtrace, BacktraceStatus},
error::Error as StdError,
};
#[derive(Debug, Serialize)]
pub struct Fault {
#[serde(serialize_with = "serialize_source")]
pub source: Option<Box<dyn StdError + Send + Sync>>,
#[serde(serialize_with = "serialize_backtrace")]
backtrace: Backtrace,
context: Option<String>,
}
impl<D> From<Fault> for Error<D> {
fn from(value: Fault) -> Self {
Error::Fault(value)
}
}
impl StdError for Fault {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.source.as_deref().map(|s| s as _)
}
}
impl std::fmt::Display for Fault {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}{}{}",
match self.backtrace.status() {
std::backtrace::BacktraceStatus::Captured =>
format!("{}\n ----------------------- \n\n", self.backtrace),
_ => String::new(),
},
match &self.context {
Some(c) => format!("Context: {}\n", c),
None => String::new(),
},
match &self.source {
Some(s) => format!(
"Source: {}, {}\n",
crate::error::errors_chain_debug(s.as_ref()),
s
),
None => String::new(),
},
)
}
}
impl Fault {
pub fn new() -> Self {
Self {
source: None,
backtrace: Backtrace::capture(),
context: None,
}
}
pub fn with_source<E: StdError + 'static + Send + Sync>(self, error: E) -> Self {
Self {
source: Some(Box::new(error)),
backtrace: self.backtrace,
context: self.context,
}
}
pub fn with_context(self, context: impl std::fmt::Display) -> Self {
Self {
source: self.source,
backtrace: self.backtrace,
context: Some(context.to_string()),
}
}
pub fn new_force() -> Self {
Self {
source: None,
backtrace: Backtrace::force_capture(),
context: None,
}
}
pub fn backtrace_status(&self) -> BacktraceStatus {
self.backtrace.status()
}
pub fn context(&self) -> Option<&str> {
self.context.as_deref()
}
}
impl Default for Fault {
fn default() -> Self {
Self::new()
}
}
fn serialize_source<S>(
source: &Option<Box<dyn StdError + Send + Sync>>,
s: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(
&source
.as_ref()
.map(|s| format!("{}: {}", crate::error::errors_chain_debug(s.as_ref()), s))
.unwrap_or_default(),
)
}
fn serialize_backtrace<S>(backtrace: &Backtrace, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(&backtrace.to_string())
}
#[cfg(test)]
mod test;