chainerror/src/lib.rs

327 lines
10 KiB
Rust
Raw Normal View History

2018-12-18 07:46:05 +01:00
pub trait ChainError: ::std::error::Error + Sized {
2018-12-18 16:21:45 +01:00
fn new(
line: u32,
filename: &'static str,
description: Option<String>,
error_cause: Option<Box<dyn std::error::Error + 'static>>,
) -> Self;
fn root_cause(&self) -> Option<&(dyn std::error::Error + 'static)>;
2018-12-18 07:46:05 +01:00
fn find_cause<T: ::std::error::Error + 'static>(
&self,
2018-12-18 16:21:45 +01:00
) -> Option<&(dyn std::error::Error + 'static)>;
}
pub trait ChainErrorFrom<T>: ChainError {
fn chain_error_from(_: T, _: u32, _: &'static str, _: Option<String>) -> Self;
}
pub trait IntoChainError<T: ChainError>: Sized {
fn into_chain_error(self, line: u32, filename: &'static str, description: Option<String>) -> T;
}
impl<T, U> IntoChainError<U> for T
where
U: ChainErrorFrom<T> + ChainError,
{
fn into_chain_error(self, line: u32, filename: &'static str, description: Option<String>) -> U {
U::chain_error_from(self, line, filename, description)
}
2018-12-18 07:46:05 +01:00
}
#[macro_export]
2018-12-18 16:21:45 +01:00
macro_rules! chain_error_fn {
2018-12-18 07:46:05 +01:00
( $t:ident, $v:expr $(, $more:expr)* ) => {
2018-12-18 16:21:45 +01:00
|e| <$t> :: new(line!(), file!(), Some(format!($v, $( $more , )* )), Some(e.into()))
2018-12-18 07:46:05 +01:00
};
( $t:path, $v:expr $(, $more:expr)* ) => {
2018-12-18 16:21:45 +01:00
|e| <$t> :: new(line!(), file!(), Some(format!($v, $( $more , )* )), Some(e.into()))
};
( $t:ident) => {
|e| <$t> :: new(line!(), file!(), None, Some(e.into()))
};
( $t:path) => {
|e| <$t> :: new(line!(), file!(), None, Some(e.into()))
2018-12-18 07:46:05 +01:00
};
}
2018-12-18 19:04:02 +01:00
#[macro_export]
macro_rules! into_boxed_chain_error_fn {
( $v:expr $(, $more:expr)* ) => {
|e| Box::<Error>::from(e).into_chain_error(line!(), file!(), Some(format!($v, $( $more , )* )))
};
( ) => {
|e| Box::<Error>::from(e).into_chain_error(line!(), file!(), None)
};
}
#[macro_export]
macro_rules! chain {
( $v:expr $(, $more:expr)* ) => {
|e| Box::<Error>::from(e).into_chain_error(line!(), file!(), Some(format!($v, $( $more , )* )))
};
( ) => {
|e| Box::<Error>::from(e).into_chain_error(line!(), file!(), None)
};
}
2018-12-18 07:46:05 +01:00
#[macro_export]
2018-12-18 16:21:45 +01:00
macro_rules! into_chain_error_fn {
2018-12-18 07:46:05 +01:00
( $v:expr $(, $more:expr)* ) => {
|e| e.into_chain_error(line!(), file!(), Some(format!($v, $( $more , )* )))
};
( ) => {
|e| e.into_chain_error(line!(), file!(), None)
};
}
#[macro_export]
2018-12-18 16:21:45 +01:00
macro_rules! chain_error_from_fn {
( $t:expr, $v:expr $(, $more:expr)* ) => {
|e| ($t).into().chain_error_from(e, line!(), file!(), Some(format!($v, $( $more , )* )))
};
( $t:expr ) => {
|e| ($t).into().chain_error_from(e, line!(), file!(), None)
};
}
#[macro_export]
macro_rules! chain_error {
2018-12-18 07:46:05 +01:00
( $t:ident, $v:expr $(, $more:expr)* ) => {
2018-12-18 16:21:45 +01:00
<$t> :: new(line!(), file!(), Some(format!($v, $( $more , )*)), None)
2018-12-18 07:46:05 +01:00
};
( $t:path, $v:expr $(, $more:expr)* ) => {
2018-12-18 16:21:45 +01:00
<$t> :: new(line!(), file!(), Some(format!($v, $( $more , )*)), None)
2018-12-18 07:46:05 +01:00
};
}
#[macro_export]
2018-12-18 16:21:45 +01:00
macro_rules! into_chain_error {
( $t:expr, $v:expr $(, $more:expr)* ) => {
$t . into_chain_error(line!(), file!(), Some(format!($v, $( $more , )*)))
};
( $t:expr ) => {
$t . into_chain_error(line!(), file!(), None)
};
}
#[macro_export]
macro_rules! derive_chain_error {
2018-12-18 07:46:05 +01:00
($e:ident) => {
pub struct $e {
line: u32,
filename: &'static str,
2018-12-18 16:21:45 +01:00
description: Option<String>,
error_cause: Option<Box<dyn std::error::Error + 'static>>,
2018-12-18 07:46:05 +01:00
}
2018-12-18 16:21:45 +01:00
impl ChainError for $e {
fn new(
2018-12-18 07:46:05 +01:00
line: u32,
filename: &'static str,
2018-12-18 16:21:45 +01:00
description: Option<String>,
error_cause: Option<Box<dyn std::error::Error + 'static>>,
2018-12-18 07:46:05 +01:00
) -> Self {
$e {
line,
filename,
description,
error_cause,
}
}
2018-12-18 16:21:45 +01:00
fn root_cause(&self) -> Option<&(dyn std::error::Error + 'static)> {
let mut cause = self as &(dyn std::error::Error + 'static);
2018-12-18 07:46:05 +01:00
while let Some(c) = cause.source() {
cause = c;
}
Some(cause)
}
fn find_cause<T: ::std::error::Error + 'static>(
&self,
2018-12-18 16:21:45 +01:00
) -> Option<&(dyn std::error::Error + 'static)> {
let mut cause = self as &(dyn std::error::Error + 'static);
2018-12-18 07:46:05 +01:00
loop {
if cause.is::<T>() {
return Some(cause);
}
match cause.source() {
Some(c) => cause = c,
None => return None,
}
}
}
}
impl ::std::error::Error for $e {
fn description(&self) -> &str {
2018-12-18 16:21:45 +01:00
if let Some(ref d) = self.description {
d.as_ref()
} else {
""
}
2018-12-18 07:46:05 +01:00
}
2018-12-18 16:21:45 +01:00
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
2018-12-18 07:46:05 +01:00
if let Some(ref e) = self.error_cause {
Some(e.as_ref())
} else {
None
}
}
}
impl ::std::fmt::Display for $e {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
2018-12-18 16:21:45 +01:00
writeln!(f, "{}", self.description())?;
2018-12-18 07:46:05 +01:00
if let Some(e) = self.source() {
writeln!(f, "\nCaused by:")?;
::std::fmt::Display::fmt(&e, f)?;
}
Ok(())
}
}
impl ::std::fmt::Debug for $e {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
2018-12-18 19:04:02 +01:00
writeln!(f, "{}:{}: {}", self.filename, self.line, self.description())?;
2018-12-18 07:46:05 +01:00
if let Some(e) = self.source() {
writeln!(f, "\nCaused by:")?;
::std::fmt::Debug::fmt(&e, f)?;
}
Ok(())
}
}
};
}
pub mod prelude {
2018-12-18 16:21:45 +01:00
pub use super::{
chain_error, chain_error_fn, chain_error_from_fn, derive_chain_error, into_chain_error,
into_chain_error_fn, ChainError, ChainErrorFrom, IntoChainError,
};
2018-12-18 07:46:05 +01:00
}
#[cfg(test)]
mod tests {
use std::error::Error;
2018-12-18 16:21:45 +01:00
use crate::prelude::*;
derive_chain_error!(MyError);
derive_chain_error!(MyMainError);
2018-12-18 07:46:05 +01:00
2018-12-18 19:04:02 +01:00
impl ChainErrorFrom<Box<Error>> for MyMainError {
fn chain_error_from(
e: Box<Error>,
line: u32,
filename: &'static str,
description: Option<String>,
) -> Self {
MyMainError {
line,
filename,
description,
error_cause: Some(e),
}
}
}
2018-12-18 07:46:05 +01:00
fn throw_error() -> Result<(), MyError> {
let directory = String::from("ldfhgdfkgjdf");
2018-12-18 16:21:45 +01:00
::std::fs::remove_dir(&directory).map_err(chain_error_fn!(
2018-12-18 07:46:05 +01:00
MyError,
"Could not remove directory '{}'{}",
&directory,
"!"
))?;
Ok(())
}
#[test]
2018-12-18 19:04:02 +01:00
fn test_chain_error_fn() -> Result<(), MyMainError> {
2018-12-18 16:21:45 +01:00
let res = throw_error().map_err(chain_error_fn!(MyMainError, "I has an error."));
2018-12-18 07:46:05 +01:00
2018-12-18 19:04:02 +01:00
if let Err(my_err) = res {
if let Some(source) = my_err.source() {
assert!(source.is::<MyError>());
}
println!("\nRoot cause is {:#?}\n", my_err.root_cause());
assert!(my_err.root_cause().unwrap().is::<::std::io::Error>());
assert!(my_err.find_cause::<::std::io::Error>().is_some());
if my_err.find_cause::<::std::io::Error>().is_some() {
println!("Has cause io::Error");
}
if my_err.find_cause::<MyError>().is_some() {
println!("Has cause MyError");
}
println!("-----------");
println!("Display Error:\n{}", my_err);
println!("-----------");
println!("Debug Error: \n{:#?}", my_err);
println!("-----------");
};
//res?;
Ok(())
}
#[test]
fn test_into_chain_error_fn() -> Result<(), MyMainError> {
let res: Result<(), MyMainError> = throw_error().map_err(into_boxed_chain_error_fn!("I has an error."));
if let Err(my_err) = res {
if let Some(source) = my_err.source() {
assert!(source.is::<MyError>());
}
println!("\nRoot cause is {:#?}\n", my_err.root_cause());
assert!(my_err.root_cause().unwrap().is::<::std::io::Error>());
assert!(my_err.find_cause::<::std::io::Error>().is_some());
if my_err.find_cause::<::std::io::Error>().is_some() {
println!("Has cause io::Error");
}
if my_err.find_cause::<MyError>().is_some() {
println!("Has cause MyError");
}
println!("-----------");
println!("Display Error:\n{}", my_err);
println!("-----------");
println!("Debug Error: \n{:#?}", my_err);
println!("-----------");
};
//res?;
Ok(())
}
#[test]
fn test_map_chain_err() -> Result<(), MyMainError> {
let res: Result<(), MyMainError> = throw_error().map_err(chain!());
2018-12-18 07:46:05 +01:00
if let Err(my_err) = res {
if let Some(source) = my_err.source() {
assert!(source.is::<MyError>());
}
println!("\nRoot cause is {:#?}\n", my_err.root_cause());
assert!(my_err.root_cause().unwrap().is::<::std::io::Error>());
assert!(my_err.find_cause::<::std::io::Error>().is_some());
if my_err.find_cause::<::std::io::Error>().is_some() {
println!("Has cause io::Error");
}
if my_err.find_cause::<MyError>().is_some() {
println!("Has cause MyError");
}
println!("-----------");
println!("Display Error:\n{}", my_err);
println!("-----------");
println!("Debug Error: \n{:#?}", my_err);
println!("-----------");
};
//res?;
Ok(())
}
}