#[macro_export]
macro_rules! impl_context_error {
($error_type:ident) => {
impl_context_error!(pub(crate) $error_type);
};
($vis:vis $error_type:ident) => {
#[derive(Debug)]
$vis struct $error_type {
message: std::borrow::Cow<'static, str>,
source: std::option::Option<Box<dyn std::error::Error + std::marker::Send + std::marker::Sync + 'static>>,
location: std::option::Option<&'static std::panic::Location<'static>>,
}
impl std::error::Error for $error_type {
fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
self.source.as_deref().map(|s| s as _)
}
}
impl std::fmt::Display for $error_type {
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 $crate::WithContext for $error_type {
fn with_context(mut self, context: $crate::Frame) -> Self {
self.source = Some(context.source);
self.location = Some(context.location);
self
}
fn location(&self) -> std::option::Option<&'static std::panic::Location<'static>> {
self.location
}
}
impl
From<(
std::borrow::Cow<'static, str>,
$crate::Frame,
)> for $error_type
{
fn from(
value: (
std::borrow::Cow<'static, str>,
$crate::Frame,
),
) -> Self {
Self {
message: value.0,
source: Some(value.1.source),
location: Some(value.1.location),
}
}
}
};
}
#[cfg(test)]
mod tests {
use crate::ext::ErrorExt;
impl_context_error!(Error);
#[test]
fn impl_error() {
let errors = Error {
message: "Failed to do something".into(),
source: Some(
Error {
message: "Failed due to internal error".into(),
source: Some(std::io::Error::other("Failed to perform IO").into()),
location: None,
}
.into(),
),
location: None,
};
assert_eq!(
errors.report().to_string(),
"Failed to do something\n|-- Failed due to internal error\n`-- Failed to perform IO"
);
}
}