use std::{
fmt::{Display, Formatter},
panic::Location,
};
use tracing::Span;
pub(crate) fn new_ctx(
message: Option<String>,
location: &'static Location<'static>,
anyhow_error: anyhow::Error,
) -> anyhow::Error {
anyhow::Error::new(Ctx {
message,
location,
span: Span::current(),
anyhow_error,
})
}
#[derive(Debug)]
pub(crate) struct Ctx {
pub(crate) message: Option<String>,
pub(crate) location: &'static Location<'static>,
pub(crate) span: Span,
pub(crate) anyhow_error: anyhow::Error,
}
impl Display for Ctx {
fn fmt(
&self,
formatter: &mut Formatter<'_>,
) -> std::fmt::Result {
match &self.message {
Some(
message,
) => formatter.write_str(message),
None => Display::fmt(&self.anyhow_error, formatter),
}
}
}
impl std::error::Error for Ctx {
fn source(
&self,
) -> Option<
&(dyn std::error::Error + 'static),
> {
Some(self.anyhow_error.as_ref())
}
}
pub trait CtxExt<T> {
fn ctx(
self,
message: impl Into<String>,
) -> anyhow::Result<T>;
fn ctx_with<F, S>(
self,
f: F,
) -> anyhow::Result<T>
where
F: FnOnce() -> S,
S: Into<String>;
fn here(
self,
) -> anyhow::Result<T>;
}
impl<
T,
E: Into<anyhow::Error>,
> CtxExt<T> for Result<T, E> {
#[track_caller]
fn ctx(
self,
message: impl Into<String>,
) -> anyhow::Result<T> {
let location = Location::caller();
self.map_err(|anyhow_error| {
new_ctx(Some(message.into()), location, anyhow_error.into())
})
}
#[track_caller]
fn ctx_with<F, S>(
self,
f: F,
) -> anyhow::Result<T>
where
F: FnOnce() -> S,
S: Into<String>,
{
let location = Location::caller();
self.map_err(|anyhow_error| {
new_ctx(Some(f().into()), location, anyhow_error.into())
})
}
#[track_caller]
fn here(
self,
) -> anyhow::Result<T> {
let location = Location::caller();
self.map_err(|anyhow_error| new_ctx(None, location, anyhow_error.into()))
}
}