use crate::SharedString;
use std::{error, fmt};
mod source;
use source::Source;
#[derive(Debug)]
pub struct Error {
message: SharedString,
source: Option<Box<Error>>,
}
impl Error {
#[inline]
pub fn new(message: impl Into<SharedString>) -> Self {
Self {
message: message.into(),
source: None,
}
}
#[inline]
pub fn with_source(message: impl Into<SharedString>, source: impl Into<Error>) -> Self {
Self {
message: message.into(),
source: Some(Box::new(source.into())),
}
}
#[inline]
pub fn context(self, message: impl Into<SharedString>) -> Self {
Self {
message: message.into(),
source: Some(Box::new(self)),
}
}
#[inline]
pub fn message(&self) -> &str {
self.message.as_ref()
}
#[inline]
pub fn source(&self) -> Option<&Error> {
self.source.as_deref()
}
#[inline]
pub fn sources(&self) -> impl Iterator<Item = &Error> {
Source::new(self)
}
#[inline]
pub fn root_source(&self) -> Option<&Error> {
self.sources().last()
}
}
impl<E: error::Error> From<E> for Error {
#[inline]
fn from(err: E) -> Self {
Self {
message: err.to_string().into(),
source: err.source().map(|err| Box::new(Self::new(err.to_string()))),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let message = &self.message;
if let Some(source) = &self.source {
let source = source.message();
let root_source = self.root_source().map(|err| err.message());
if root_source != Some(source) {
tracing::error!(root_source, source, "{message}");
} else {
tracing::error!(root_source, "{message}");
}
} else {
tracing::error!("{message}");
}
write!(f, "{message}")
}
}
#[macro_export]
macro_rules! bail {
($message:literal $(,)?) => {{
tracing::warn!($message);
return Err(Error::new($message));
}};
($err:expr $(,)?) => {{
tracing::warn!($err);
return Err(Error::from($err));
}};
($fmt:expr, $($arg:tt)+) => {{
let message = format!($fmt, $($arg)+);
tracing::warn!(message);
return Err(Error::new(message));
}};
}
#[macro_export]
macro_rules! warn {
($message:literal $(,)?) => {{
tracing::warn!($message);
Error::new($message)
}};
($err:expr $(,)?) => {{
tracing::warn!($err);
Error::from($err)
}};
($fmt:expr, $($arg:tt)+) => {{
let message = format!($fmt, $($arg)+);
tracing::warn!(message);
Error::new(message)
}};
}