add more traits and macros

Namely:
* ChainErrorFrom
* IntoChainError
* minto_cherr!
* into_cherr!
* strerr!

Also make derive_str_cherr! struct public
This commit is contained in:
Harald Hoyer 2019-01-08 15:33:12 +01:00
parent f972b5f703
commit a040044529
4 changed files with 104 additions and 5 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "chainerror" name = "chainerror"
version = "0.2.0" version = "0.3.0"
authors = ["Harald Hoyer <harald@redhat.com>"] authors = ["Harald Hoyer <harald@redhat.com>"]
edition = "2018" edition = "2018"
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"

View file

@ -30,6 +30,7 @@ impl ::std::fmt::Display for Func1ErrorKind {
} }
} }
} }
impl ::std::error::Error for Func1ErrorKind {}
fn func1() -> ChainResult<(), Func1ErrorKind> { fn func1() -> ChainResult<(), Func1ErrorKind> {
func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?; func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?;

View file

@ -36,6 +36,8 @@ impl ::std::fmt::Debug for Func1ErrorKind {
} }
} }
impl ::std::error::Error for Func1ErrorKind {}
fn func1() -> ChainResult<(), Func1ErrorKind> { fn func1() -> ChainResult<(), Func1ErrorKind> {
func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?; func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?;
let filename = String::from("bar.txt"); let filename = String::from("bar.txt");

View file

@ -369,7 +369,7 @@ impl<T: 'static + Display + Debug> ChainError<T> {
~~~ ~~~
**/ **/
pub fn kind<'a>(&'a self) -> &'a T { pub fn kind(&self) -> &T {
&self.kind &self.kind
} }
} }
@ -395,7 +395,10 @@ impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> {
fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> {
if self.is_chain::<T>() { if self.is_chain::<T>() {
unsafe { Some(&*(self as *const dyn Error as *const &ChainError<T>)) } #[allow(clippy::cast_ptr_alignment)]
unsafe {
Some(&*(self as *const dyn Error as *const &ChainError<T>))
}
} else { } else {
None None
} }
@ -403,7 +406,10 @@ impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> {
fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
if self.is_chain::<T>() { if self.is_chain::<T>() {
unsafe { Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) } #[allow(clippy::cast_ptr_alignment)]
unsafe {
Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>))
}
} else { } else {
None None
} }
@ -523,6 +529,48 @@ impl<T: 'static + Display + Debug> Debug for ChainError<T> {
} }
} }
pub trait ChainErrorFrom<T>: Sized {
fn chain_error_from(_: T, line_filename: Option<(u32, &'static str)>) -> ChainError<Self>;
}
pub trait IntoChainError<T>: Sized {
fn into_chain_error(self, line_filename: Option<(u32, &'static str)>) -> ChainError<T>;
}
impl<T, U> IntoChainError<U> for T
where
U: ChainErrorFrom<T>,
{
fn into_chain_error(self, line_filename: Option<(u32, &'static str)>) -> ChainError<U> {
U::chain_error_from(self, line_filename)
}
}
impl<T, U> ChainErrorFrom<T> for U
where
T: Into<U>,
U: 'static + Display + Debug,
{
fn chain_error_from(t: T, line_filename: Option<(u32, &'static str)>) -> ChainError<Self> {
let e: U = t.into();
ChainError::<_>::new(e, None, line_filename)
}
}
#[macro_export]
macro_rules! minto_cherr {
( ) => {
|e| e.into_chain_error(Some((line!(), file!())))
};
}
#[macro_export]
macro_rules! into_cherr {
( $t:expr ) => {
$t.into_chain_error(Some((line!(), file!())))
};
}
/** creates a new `ChainError<T>` /** creates a new `ChainError<T>`
# Examples # Examples
@ -719,6 +767,54 @@ macro_rules! mstrerr {
}; };
} }
/** convenience macro for cherr!(T(format!(…))) where T(String)
~~~rust
# use crate::chainerror::*;
# use std::error::Error;
# use std::result::Result;
#
derive_str_cherr!(Func2Error);
fn func2() -> ChainResult<(), Func2Error> {
let filename = "foo.txt";
Err(strerr!(Func2Error, "Error reading '{}'", filename))
}
derive_str_cherr!(Func1Error);
fn func1() -> Result<(), Box<Error>> {
func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
Ok(())
}
#
# fn main() {
# if let Err(e) = func1() {
# if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
# assert!(f1err.find_cause::<ChainError<Func2Error>>().is_some());
# assert!(f1err.find_chain_cause::<Func2Error>().is_some());
# } else {
# panic!();
# }
# } else {
# unreachable!();
# }
# }
~~~
**/
#[macro_export]
macro_rules! strerr {
( $t:ident, $v:expr $(, $more:expr)* ) => {
cherr!($t (format!($v, $( $more , )* )))
};
( $t:path, $v:expr $(, $more:expr)* ) => {
cherr!($t (format!($v, $( $more , )* )))
};
( $v:expr $(, $more:expr)* ) => {
cherr!(format!($v, $( $more , )* ))
};
}
/** convenience macro to create a "new type" T(String) and implement Display + Debug for T /** convenience macro to create a "new type" T(String) and implement Display + Debug for T
~~~rust ~~~rust
@ -765,7 +861,7 @@ fn func1() -> Result<(), Box<Error>> {
#[macro_export] #[macro_export]
macro_rules! derive_str_cherr { macro_rules! derive_str_cherr {
($e:ident) => { ($e:ident) => {
struct $e(String); pub struct $e(pub String);
impl ::std::fmt::Display for $e { impl ::std::fmt::Display for $e {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{}", self.0) write!(f, "{}", self.0)