#[doc(hidden)]
pub mod internal;
use std::{error, fmt};
pub type Error = Box<dyn error::Error + Send + Sync + 'static>;
pub type ErrorRef<'a> = &'a (dyn error::Error + 'static);
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
struct ErrorChain<'a> {
error: Box<dyn error::Error + 'a>,
}
impl fmt::Display for ErrorChain<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut error = self.error.as_ref();
write!(f, "{}", &error)?;
if f.alternate() {
if let Some(first_inner) = error.source() {
writeln!(f, "\n\nCaused by:")?;
if let Some(second_inner) = first_inner.source() {
writeln!(f, "{: >5}: {}", 0, first_inner)?;
write!(f, "{: >5}: {}", 1, second_inner)?;
error = second_inner;
let mut n = 2;
while let Some(inner) = error.source() {
write!(f, "\n{: >5}: {}", n, inner)?;
error = inner;
n += 1;
}
} else {
write!(f, " {}", first_inner)?;
}
}
} else {
while let Some(inner) = error.source() {
write!(f, ": {}", inner)?;
error = inner;
}
}
Ok(())
}
}
#[must_use]
#[inline]
pub fn print_error_chain<'a>(error: impl error::Error + 'a) -> impl fmt::Display + 'a {
ErrorChain {
error: Box::new(error),
}
}
pub struct MainError {
error: Error,
}
impl fmt::Display for MainError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:#}", print_error_chain(self.error.as_ref()))
}
}
impl fmt::Debug for MainError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:#}", print_error_chain(self.error.as_ref()))
}
}
impl<E: Into<Error>> From<E> for MainError {
fn from(error: E) -> Self {
Self {
error: error.into(),
}
}
}
pub type MainResult = std::result::Result<(), MainError>;
#[macro_export]
macro_rules! err {
($fmt:expr) => {
$crate::internal::error_from_args(::std::format_args!($fmt))
};
($fmt:expr, $($args:tt)*) => {
$crate::internal::error_from_args(::std::format_args!($fmt, $($args)*))
};
}
#[macro_export]
macro_rules! bail {
($($arg:tt)*) => {
return Err(::std::convert::Into::into($crate::err!($($arg)*)));
};
}
#[macro_export]
macro_rules! wrap {
($source:expr, $fmt:expr) => {
$crate::internal::wrap_error_from_args($source, ::std::format_args!($fmt))
};
($source:expr, $fmt:expr, $($args:tt)*) => {
$crate::internal::wrap_error_from_args($source, ::std::format_args!($fmt, $($args)*))
};
}
#[inline]
pub fn to_err(error: impl Into<Error>) -> impl error::Error + Send + Sync + 'static {
internal::WrapError {
inner: error.into(),
}
}
#[cfg(test)]
mod tests {
use std::ops::Deref;
#[test]
fn error_types() {
let sample_error = std::fs::metadata("oihaoidbo89ya7dsuhaod8atntdao7sdy").unwrap_err();
let owned_error: crate::Error = sample_error.into();
let _error_ref: crate::ErrorRef = owned_error.as_ref();
let _error_ref_2: crate::ErrorRef = owned_error.deref();
}
#[test]
fn messages() {
let e = crate::err!("unknown error");
let _e2 = crate::err!("unknown error {}{3}{1}{2}{1}", 7, 3, 5, 1);
let e = crate::wrap!(e, "te{}{}", "st", 1);
let e = crate::wrap!(e, "outer test");
let printed = crate::print_error_chain(e);
assert_eq!(printed.to_string(), "outer test: test1: unknown error");
}
#[test]
fn formatted() {
let e = crate::err!("hello {}", "world");
let owned: crate::Error = e.into();
assert_eq!(owned.to_string(), "hello world");
}
fn test_bail_main_result() -> crate::MainResult {
crate::bail!("test bail");
}
#[test]
fn test_main_result_format() {
let e = test_bail_main_result().unwrap_err();
assert_eq!(format!("Error: {:?}", e), "Error: test bail");
}
fn test_bail() -> Result<(), crate::Error> {
crate::bail!("bailing");
}
#[test]
fn to_err_tests() {
let error: crate::Error = test_bail().unwrap_err();
let actual_error = crate::to_err(error);
let actual_error_2 = crate::to_err(actual_error);
assert_eq!(
crate::print_error_chain(&actual_error_2).to_string(),
"bailing"
);
}
#[test]
fn test_wrap_io_err() {
std::fs::File::open("hello")
.map_err(|e| wrap!(e, "error"))
.unwrap_err();
}
fn _return_wrap() -> Result<(), crate::Error> {
let e = err!("hi");
Err(wrap!(e, "wrap"))?;
Ok(())
}
#[test]
fn multiline_error_chain() {
let e = crate::err!("unknown error {}", 7);
let e = crate::wrap!(e, "unknown error {}", 7);
let e = crate::wrap!(e, "unknown error {}", 18);
assert_eq!(
format!("{:#}", crate::print_error_chain(e)),
"unknown error 18
Caused by:
0: unknown error 7
1: unknown error 7"
);
}
#[test]
fn multline_single_error() {
let e = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "oh no");
assert_eq!(format!("{:#}", crate::print_error_chain(e)), "oh no");
}
#[test]
fn multline_two_errors() {
let e = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "oh no");
let e = crate::wrap!(e, "permission denied");
assert_eq!(
format!("{:#}", crate::print_error_chain(e)),
"permission denied
Caused by:
oh no"
);
}
#[test]
fn more_than_ten_errors() {
let mut e: crate::Error =
std::io::Error::new(std::io::ErrorKind::PermissionDenied, "oh no").into();
for i in 0..12 {
e = crate::wrap!(e, "permission denied {}", i).into();
}
assert_eq!(
format!("{:#}", crate::print_error_chain(e.as_ref())),
"permission denied 11
Caused by:
0: permission denied 10
1: permission denied 9
2: permission denied 8
3: permission denied 7
4: permission denied 6
5: permission denied 5
6: permission denied 4
7: permission denied 3
8: permission denied 2
9: permission denied 1
10: permission denied 0
11: oh no"
);
}
}