use std::fmt::Display;
use tracing_error::SpanTrace;
#[derive(Debug)]
pub struct ICError<E> {
pub kind: E,
pub context: SpanTrace,
}
impl<E> ICError<E> {
pub fn capture(kind: E) -> Self {
Self { kind, context: SpanTrace::capture() }
}
pub fn kind(&self) -> &E {
&self.kind
}
pub fn span(&self) -> &SpanTrace {
&self.context
}
pub fn inject<F>(self) -> ICError<F>
where
E: Into<F>,
{
ICError { kind: self.kind.into(), context: self.context }
}
}
impl<E: Display> Display for ICError<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.kind.fmt(f)?;
write!(f, "\n\ncontext:\n{}\n", self.context)?;
Ok(())
}
}
impl<E: std::error::Error + 'static> std::error::Error for ICError<E> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.kind)
}
fn cause(&self) -> Option<&dyn std::error::Error> {
self.source()
}
}
pub trait ICResultExt<T, E> {
fn capture<Kind>(self) -> Result<T, ICError<Kind>>
where
E: Into<Kind>;
fn capture_box<Kind>(self) -> Result<T, ICError<Kind>>
where
E: std::error::Error + Send + Sync + 'static,
Box<dyn std::error::Error + Send + Sync + 'static>: Into<Kind>;
}
impl<T, E> ICResultExt<T, E> for Result<T, E> {
fn capture<Kind>(self) -> Result<T, ICError<Kind>>
where
E: Into<Kind>,
{
self.map_err(|e| ICError::capture(e.into()))
}
fn capture_box<Kind>(self) -> Result<T, ICError<Kind>>
where
E: std::error::Error + Send + Sync + 'static,
Box<dyn std::error::Error + Send + Sync + 'static>: Into<Kind>,
{
self.map_err(|e| {
let e: Box<dyn std::error::Error + Send + Sync + 'static> = Box::new(e);
ICError::capture(e.into())
})
}
}
pub trait ICResultCtxExt<T, E> {
fn inject<Kind>(self) -> Result<T, ICError<Kind>>
where
E: Into<Kind>;
}
impl<T, E> ICResultCtxExt<T, E> for Result<T, ICError<E>> {
fn inject<Kind>(self) -> Result<T, ICError<Kind>>
where
E: Into<Kind>,
{
self.map_err(|e| e.inject())
}
}