mod backtrace;
mod error;
mod wrapper;
use std::error::Error as StdError;
pub use crate::{error::Error, wrapper::Wrapper};
pub type Result<T, E=Error> = std::result::Result<T, E>;
pub mod prelude {
pub use super::{bail, err, match_err, wrap, Error, Result, Wrapper};
pub use std::any::TypeId;
}
#[macro_export]
macro_rules! bail {
($msg:expr) => {
return $crate::Error::new($msg);
};
($fmt:expr, $($arg:tt)*) => {
return $crate::Error::new(&format!($fmt, $($arg)*));
};
}
#[macro_export]
macro_rules! err {
($msg:expr) => {
$crate::Error::raw($msg);
};
($fmt:expr, $($arg:tt)*) => {
$crate::Error::raw(&format!($fmt, $($arg)*));
};
}
#[macro_export]
macro_rules! wrap {
($err:expr, $msg:expr) => {
return $crate::Error::wrap($err, $msg);
};
($err:expr, $fmt:expr, $($arg:tt)*) => {
return $crate::Error::wrap($err, &format!($fmt, $($arg)*));
};
}
#[macro_export]
macro_rules! match_err {
($err:expr, { $($var:ident : $kind:ty => $arm:expr),*, _ => $default:expr }) => (
$(
if ($err as &(dyn std::error::Error + 'static)).is::<$kind>() {
let $var = ($err as &(dyn std::error::Error + 'static)).downcast_ref::<$kind>().unwrap();
$arm
} else
)*
{
$default
}
)
}
#[cfg(test)]
mod tests {
use super::*;
use std::{env, fmt, io};
use std::sync::Once;
static INIT: Once = Once::new();
pub fn initialize() {
INIT.call_once(|| {
env::set_var(gory::TERM_COLOR, "0");
env::set_var("RUST_BACKTRACE", "0");
});
}
#[derive(Debug)]
struct TestError1(String);
impl std::error::Error for TestError1 {}
impl fmt::Display for TestError1 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug)]
struct TestError2(String);
impl std::error::Error for TestError2 {}
impl fmt::Display for TestError2 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
fn bail_simple() -> Result<()> {
bail!("oh no!");
}
fn bail_formatted() -> Result<()> {
bail!("foo: {}", "oh no!");
}
fn wrap_simple() -> Result<()> {
wrap!(io::Error::new(io::ErrorKind::NotFound, "oh no!"), "simple_wrap");
}
fn wrap_formatted() -> Result<()> {
wrap!(io::Error::new(io::ErrorKind::NotFound, "oh no!"), "foo: {}", "simple_wrap");
}
#[test]
fn test_bail() {
initialize();
assert_eq!("oh no!", bail_simple().unwrap_err().to_string());
assert_eq!("foo: oh no!", bail_formatted().unwrap_err().to_string());
}
#[test]
fn test_err() {
initialize();
assert_eq!("oh no!", err!("oh no!").to_string());
assert_eq!("foo: oh no!", err!("foo: {}", "oh no!").to_string());
}
#[test]
fn test_wrap() {
initialize();
assert_eq!("simple_wrap", format!("{}", wrap_simple().unwrap_err()));
assert_eq!(" error: simple_wrap\n cause: oh no!", format!("{:#}", wrap_simple().unwrap_err()));
assert_eq!("foo: simple_wrap", wrap_formatted().unwrap_err().to_string());
assert_eq!(" error: foo: simple_wrap\n cause: oh no!", format!("{:#}", wrap_formatted().unwrap_err()));
}
#[test]
fn test_single() {
initialize();
let err = io::Error::new(std::io::ErrorKind::Other, "oh no!");
let res = match_err!(&err, {
_x: io::Error => true,
_ => false
});
assert!(res);
}
#[test]
fn test_match_err() {
initialize();
let errors: Vec<Box<dyn std::error::Error>> = vec![
Box::new(TestError1("test1".to_string())),
Box::new(TestError2("test2".to_string())),
Box::new(io::Error::new(std::io::ErrorKind::Other, "test3")),
];
let mut buf = String::new();
for boxed in errors.iter() {
let err: &(dyn StdError+'static) = &**boxed;
buf += &match_err!(err, {
x: io::Error => format!("io::Error: {}\n", x),
x: TestError1 => format!("TestError1: {}\n", x),
x: TestError2 => format!("TestError2: {}\n", x),
_ => format!("no match")
});
}
assert_eq!("TestError1: test1\nTestError2: test2\nio::Error: test3\n", buf);
}
}