macro hygiene

This commit is contained in:
Harald Hoyer 2019-03-13 11:34:53 +01:00
parent a558c35ba4
commit ae6c01aab2
No known key found for this signature in database
GPG key ID: 340F12141EA0994D
6 changed files with 372 additions and 68 deletions

View file

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

View file

@ -1,8 +1,9 @@
use chainerror::*;
use std::error::Error;
use std::io;
use std::result::Result;
use chainerror::*;
fn do_some_io() -> Result<(), Box<Error + Send + Sync>> {
Err(io::Error::from(io::ErrorKind::NotFound))?;
Ok(())

View file

@ -15,7 +15,7 @@ pub mod mycrate {
Ok(())
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum ErrorKind {
Func2,
IO(String),
@ -42,7 +42,7 @@ pub mod mycrate {
}
}
fn main() -> Result<(), Box<std::error::Error>> {
fn main() -> Result<(), Box<std::error::Error + Send + Sync>> {
use mycrate::func1;
use mycrate::ErrorKind;
use std::error::Error;

View file

@ -182,7 +182,7 @@ pub mod mycrate {
}
}
fn main() -> Result<(), Box<std::error::Error>> {
fn main() -> Result<(), Box<std::error::Error + Send + Sync>> {
use mycrate::func1;
use mycrate::ErrorKind;
use std::error::Error;

139
examples/tutorial15.rs Normal file
View file

@ -0,0 +1,139 @@
pub mod mycrate {
use std::io;
use chainerror::*;
fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> {
Err(io::Error::from(io::ErrorKind::NotFound))?;
Ok(())
}
derive_str_cherr!(Func2Error);
fn func2() -> std::result::Result<(), Box<std::error::Error + Send + Sync>> {
let filename = "foo.txt";
do_some_io(filename)
.map_err(|e| cherr!(e, Func2Error(format!("Error reading '{}'", filename))))?;
Ok(())
}
#[derive(Debug, Clone)]
pub enum ErrorKind {
Func2,
IO(String),
FatalError(String),
Unknown,
}
derive_err_kind!(Error, ErrorKind);
pub type Result<T> = std::result::Result<T, Error>;
impl std::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result {
match self {
ErrorKind::FatalError(e) => write!(f, "fatal error {}", e),
ErrorKind::Unknown => write!(f, "unknown error"),
ErrorKind::Func2 => write!(f, "func1 error calling func2"),
ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename),
}
}
}
impl ErrorKind {
fn from_io_error(e: &io::Error, f: String) -> Self {
match e.kind() {
io::ErrorKind::BrokenPipe => panic!("Should not happen"),
io::ErrorKind::ConnectionReset => {
ErrorKind::FatalError(format!("While reading `{}`: {}", f, e))
}
_ => ErrorKind::IO(f),
}
}
}
impl From<&(dyn std::error::Error + 'static + Send + Sync)> for ErrorKind {
fn from(e: &(dyn std::error::Error + 'static + Send + Sync)) -> Self {
if let Some(_) = e.downcast_ref::<io::Error>() {
ErrorKind::IO(String::from("Unknown filename"))
} else if let Some(_) = e.downcast_inner_ref::<Func2Error>() {
ErrorKind::Func2
} else {
ErrorKind::Unknown
}
}
}
impl From<&std::boxed::Box<dyn std::error::Error + 'static + Send + Sync>> for ErrorKind {
fn from(e: &std::boxed::Box<dyn std::error::Error + 'static + Send + Sync>) -> Self {
Self::from(&**e)
}
}
impl From<&Func2Error> for ErrorKind {
fn from(_: &Func2Error) -> Self {
ErrorKind::Func2
}
}
impl From<&io::Error> for ErrorKind {
fn from(e: &io::Error) -> Self {
ErrorKind::IO(format!("{}", e))
}
}
pub fn func1() -> Result<()> {
func2().map_err(|e| cherr!(e, ErrorKind::from(&e)))?;
let filename = "bar.txt";
do_some_io(filename)
.map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?;
do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?;
do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?;
do_some_io(filename).map_err(minto_cherr!(ErrorKind))?;
Ok(())
}
pub fn super_func1() -> Result<()> {
func1().map_err(minto_cherr!(ErrorKind))?;
Ok(())
}
}
fn main() -> Result<(), Box<std::error::Error + Send + Sync>> {
use mycrate::super_func1;
use mycrate::ErrorKind;
use std::error::Error;
use std::io;
if let Err(e) = super_func1() {
match e.kind() {
ErrorKind::FatalError(f) => eprintln!("Main Error Report: Fatal Error: {}", f),
ErrorKind::Unknown => eprintln!("Main Error Report: Unknown error occurred"),
ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"),
ErrorKind::IO(ref filename) => {
eprintln!("Main Error Report: func1 error reading '{}'", filename)
}
}
eprintln!();
let mut s: &Error = &e;
while let Some(c) = s.source() {
if let Some(ioerror) = c.downcast_ref::<io::Error>() {
eprintln!("caused by: std::io::Error: {}", ioerror);
match ioerror.kind() {
io::ErrorKind::NotFound => eprintln!("of kind: std::io::ErrorKind::NotFound"),
_ => {}
}
} else {
eprintln!("caused by: {}", c);
}
s = c;
}
eprintln!("\nDebug Error:\n{:?}", e);
}
Ok(())
}

View file

@ -174,7 +174,6 @@
keyword_idents,
macro_use_extern_crate,
missing_debug_implementations,
missing_docs,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
@ -243,10 +242,10 @@ impl<T: 'static + Display + Debug> ChainError<T> {
/// # Examples
///
/// ```rust
/// # use crate::chainerror::*;
/// # use std::error::Error;
/// # use std::io;
/// # use std::result::Result;
/// use chainerror::*;
/// use std::error::Error;
/// use std::io;
///
/// fn do_some_io() -> Result<(), Box<Error + Send + Sync>> {
/// Err(io::Error::from(io::ErrorKind::NotFound))?;
/// Ok(())
@ -296,7 +295,7 @@ impl<T: 'static + Display + Debug> ChainError<T> {
/// # Examples
///
/// ```rust
/// # use chainerror::{ChainError, derive_str_cherr};
/// # use chainerror::*;
/// # derive_str_cherr!(FooError);
/// # let err = ChainError::new(String::new(), None, None);
/// // Instead of writing
@ -319,7 +318,7 @@ impl<T: 'static + Display + Debug> ChainError<T> {
/// # Examples
///
/// ```rust
/// # use chainerror::{ChainError, derive_str_cherr};
/// # use chainerror::*;
/// # derive_str_cherr!(FooErrorKind);
/// # let err = ChainError::new(String::new(), None, None);
/// // Instead of writing
@ -348,10 +347,10 @@ impl<T: 'static + Display + Debug> ChainError<T> {
/// # Examples
///
/// ```rust
/// # use crate::chainerror::*;
/// # use std::error::Error;
/// # use std::io;
/// # use std::result::Result;
/// use chainerror::*;
/// use std::error::Error;
/// use std::io;
///
/// fn do_some_io() -> Result<(), Box<Error + Send + Sync>> {
/// Err(io::Error::from(io::ErrorKind::NotFound))?;
/// Ok(())
@ -449,6 +448,10 @@ pub trait ChainErrorDown {
fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>;
/// Downcast to a mutable reference of `ChainError<T>`
fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>;
/// Downcast to T of `ChainError<T>`
fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T>;
/// Downcast to T mutable reference of `ChainError<T>`
fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T>;
}
impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> {
@ -482,6 +485,31 @@ impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> {
None
}
}
#[inline]
fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
if self.is_chain::<T>() {
#[allow(clippy::cast_ptr_alignment)]
unsafe {
#[allow(trivial_casts)]
Some(&(*(self as *const dyn Error as *const &ChainError<T>)).kind)
}
} else {
None
}
}
#[inline]
fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
if self.is_chain::<T>() {
#[allow(clippy::cast_ptr_alignment)]
unsafe {
#[allow(trivial_casts)]
Some(&mut (*(self as *mut dyn Error as *mut &mut ChainError<T>)).kind)
}
} else {
None
}
}
}
impl ChainErrorDown for dyn Error + 'static {
@ -499,6 +527,22 @@ impl ChainErrorDown for dyn Error + 'static {
fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
self.downcast_mut::<ChainError<T>>()
}
#[inline]
fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
self.downcast_ref::<T>()
.or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
}
#[inline]
fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
return self.downcast_mut::<T>();
}
self.downcast_mut::<ChainError<T>>()
.and_then(|e| e.downcast_inner_mut::<T>())
}
}
impl ChainErrorDown for dyn Error + 'static + Send {
@ -516,6 +560,22 @@ impl ChainErrorDown for dyn Error + 'static + Send {
fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
self.downcast_mut::<ChainError<T>>()
}
#[inline]
fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
self.downcast_ref::<T>()
.or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
}
#[inline]
fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
return self.downcast_mut::<T>();
}
self.downcast_mut::<ChainError<T>>()
.and_then(|e| e.downcast_inner_mut::<T>())
}
}
impl ChainErrorDown for dyn Error + 'static + Send + Sync {
@ -533,26 +593,48 @@ impl ChainErrorDown for dyn Error + 'static + Send + Sync {
fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> {
self.downcast_mut::<ChainError<T>>()
}
#[inline]
fn downcast_inner_ref<T: 'static + Error>(&self) -> Option<&T> {
self.downcast_ref::<T>()
.or_else(|| self.downcast_ref::<ChainError<T>>().map(|e| e.kind()))
}
#[inline]
fn downcast_inner_mut<T: 'static + Error>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
return self.downcast_mut::<T>();
}
self.downcast_mut::<ChainError<T>>()
.and_then(|e| e.downcast_inner_mut::<T>())
}
}
impl<T: 'static + Display + Debug> Error for ChainError<T> {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.error_cause.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static))
self.error_cause
.as_ref()
.map(|e| e.as_ref() as &(dyn Error + 'static))
}
}
impl<T: 'static + Display + Debug> Error for &ChainError<T> {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.error_cause.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static))
self.error_cause
.as_ref()
.map(|e| e.as_ref() as &(dyn Error + 'static))
}
}
impl<T: 'static + Display + Debug> Error for &mut ChainError<T> {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.error_cause.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static))
self.error_cause
.as_ref()
.map(|e| e.as_ref() as &(dyn Error + 'static))
}
}
@ -629,28 +711,34 @@ where
#[inline]
fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> {
let e: U = t.into();
ChainError::<_>::new(e, None, line_filename)
ChainError::new(e, None, line_filename)
}
}
/// map into `ChainError<T>` with `IntoChainError`
/*
impl<T, U> ChainErrorFrom<T> for U
where
T: 'static + Error + Into<Box<T>> + Clone,
U: 'static + Display + Debug + From<T>,
{
#[inline]
fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError<Self> {
ChainError::new(U::from(t.clone()), Some(Box::from(t)), line_filename)
}
}
*/
/// map into `ChainError<T>` with `T::from(err)`
///
/// adds `line!()` and `file!()` information
#[macro_export]
macro_rules! minto_cherr {
( ) => {
|e| ChainErrorFrom::chain_error_from(e, Some(concat!(file!(), ":", line!(), ": ")))
};
}
/// into `ChainError<T>` with `IntoChainError`
///
/// adds `line!()` and `file!()` information
#[macro_export]
macro_rules! into_cherr {
( $t:expr ) => {
ChainErrorFrom::chain_error_from($t, Some(concat!(file!(), ":", line!(), ": ")))
};
( $k:ident ) => (
|e| $crate::cherr!(e, $k::from(&e))
);
( $enum:ident $(:: $enum_path:ident)* ) => (
|e| $crate::cherr!(e, $enum $(:: $enum_path)*::from(&e))
);
}
/// Creates a new `ChainError<T>`
@ -734,32 +822,23 @@ macro_rules! into_cherr {
#[macro_export]
macro_rules! cherr {
( $k:expr ) => ({
ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": ")))
$crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": ")))
});
( None, $k:expr ) => ({
ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": ")))
$crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": ")))
});
( None, $fmt:expr, $($arg:tt)+ ) => ({
cherr!(None, format!($fmt, $($arg)+ ))
$crate::cherr!(None, format!($fmt, $($arg)+ ))
});
( None, $fmt:expr, $($arg:tt)+ ) => ({
cherr!(None, format!($fmt, $($arg)+ ))
$crate::cherr!(None, format!($fmt, $($arg)+ ))
});
( $e:path, $k:expr ) => ({
ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": ")))
$crate::ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": ")))
});
( $e:path, $fmt:expr, $($arg:tt)+ ) => ({
cherr!($e, format!($fmt, $($arg)+ ))
$crate::cherr!($e, format!($fmt, $($arg)+ ))
});
}
/// shortcut for |e| cherr!(e, $k)
#[macro_export]
macro_rules! mcherr {
( $k:expr ) => {{
|e| cherr!(e, $k)
}};
}
/// Convenience macro for `|e| cherr!(e, format!(…))`
@ -846,22 +925,22 @@ macro_rules! mcherr {
#[macro_export]
macro_rules! mstrerr {
( $t:path, $msg:expr ) => ({
|e| cherr!(e, $t ($msg.to_string()))
|e| $crate::cherr!(e, $t ($msg.to_string()))
});
( $t:path, $msg:expr, ) => ({
|e| cherr!(e, $t ($msg.to_string()))
|e| $crate::cherr!(e, $t ($msg.to_string()))
});
( $t:path, $fmt:expr, $($arg:tt)+ ) => ({
|e| cherr!(e, $t (format!($fmt, $($arg)+ )))
|e| $crate::cherr!(e, $t (format!($fmt, $($arg)+ )))
});
($msg:expr) => ({
|e| cherr!(e, $msg.to_string())
|e| $crate::cherr!(e, $msg.to_string())
});
($msg:expr, ) => ({
|e| cherr!(e, $msg.to_string())
|e| $crate::cherr!(e, $msg.to_string())
});
($fmt:expr, $($arg:tt)+) => ({
|e| cherr!(e, format!($fmt, $($arg)+ ))
|e| $crate::cherr!(e, format!($fmt, $($arg)+ ))
});
}
@ -882,7 +961,7 @@ macro_rules! mstrerr {
///
/// derive_str_cherr!(Func1Error);
///
/// fn func1() -> Result<(), Box<Error + Send + Sync>> {
/// fn func1() -> Result<(), Box<Error>> {
/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
/// Ok(())
/// }
@ -902,22 +981,22 @@ macro_rules! mstrerr {
#[macro_export]
macro_rules! strerr {
( $t:path, $msg:expr ) => ({
cherr!($t ($msg.to_string()))
$crate::cherr!($t ($msg.to_string()))
});
( $t:path, $msg:expr, ) => ({
cherr!($t ($msg.to_string()))
$crate::cherr!($t ($msg.to_string()))
});
( $t:path, $fmt:expr, $($arg:tt)+ ) => ({
cherr!($t (format!($fmt, $($arg)+ )))
$crate::cherr!($t (format!($fmt, $($arg)+ )))
});
($msg:expr) => ({
cherr!($msg.to_string())
$crate::cherr!($msg.to_string())
});
($msg:expr, ) => ({
cherr!($msg.to_string())
$crate::cherr!($msg.to_string())
});
($fmt:expr, $($arg:tt)+) => ({
cherr!(format!($fmt, $($arg)+ ))
$crate::cherr!(format!($fmt, $($arg)+ ))
});
}
@ -944,7 +1023,7 @@ macro_rules! strerr {
///
/// derive_str_cherr!(Func1Error);
///
/// fn func1() -> Result<(), Box<Error + Send + Sync>> {
/// fn func1() -> Result<(), Box<Error>> {
/// func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
/// Ok(())
/// }
@ -980,13 +1059,76 @@ macro_rules! derive_str_cherr {
};
}
/// Derive an Error, which wraps ChainError and implements a kind() method
/// Derive an Error for an ErrorKind, which wraps a `ChainError` and implements a `kind()` method
///
/// e.kind() returns the kind
/// It basically hides `ChainError` to the outside and only exposes the `kind()`
/// method.
///
/// Error::kind() returns the ErrorKind
/// Error::source() returns the parent error
///
/// # Examples
///
/// ```rust
/// use std::io;
/// use chainerror::*;
///
/// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> {
/// return Err(io::Error::from(io::ErrorKind::NotFound));
/// }
///
/// #[derive(Debug, Clone)]
/// pub enum ErrorKind {
/// IO(String),
/// FatalError(String),
/// Unknown,
/// }
///
/// derive_err_kind!(Error, ErrorKind);
///
/// impl std::fmt::Display for ErrorKind {
/// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result {
/// match self {
/// ErrorKind::FatalError(e) => write!(f, "fatal error {}", e),
/// ErrorKind::Unknown => write!(f, "unknown error"),
/// ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename),
/// }
/// }
/// }
///
/// impl ErrorKind {
/// fn from_io_error(e: &io::Error, f: String) -> Self {
/// match e.kind() {
/// io::ErrorKind::BrokenPipe => panic!("Should not happen"),
/// io::ErrorKind::ConnectionReset => {
/// ErrorKind::FatalError(format!("While reading `{}`: {}", f, e))
/// }
/// _ => ErrorKind::IO(f),
/// }
/// }
/// }
///
/// impl From<&io::Error> for ErrorKind {
/// fn from(e: &io::Error) -> Self {
/// ErrorKind::IO(format!("{}", e))
/// }
/// }
///
/// pub fn func1() -> std::result::Result<(), Error> {
/// let filename = "bar.txt";
///
/// do_some_io(filename)
/// .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?;
/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?;
/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?;
/// do_some_io(filename).map_err(minto_cherr!(ErrorKind))?;
/// Ok(())
/// }
/// ```
#[macro_export]
macro_rules! derive_err_kind {
($e:ident, $k:ident) => {
pub struct $e(ChainError<$k>);
pub struct $e($crate::ChainError<$k>);
impl $e {
pub fn kind(&self) -> &$k {
@ -996,16 +1138,38 @@ macro_rules! derive_err_kind {
impl From<$k> for $e {
fn from(e: $k) -> Self {
$e(ChainError::new(e, None, None))
$e($crate::ChainError::new(e, None, None))
}
}
impl From<ChainError<$k>> for $e {
fn from(e: ChainError<$k>) -> Self {
fn from(e: $crate::ChainError<$k>) -> Self {
$e(e)
}
}
impl From<&$e> for $k
where
$k: Clone,
{
fn from(e: &$e) -> Self {
e.kind().clone()
}
}
impl $crate::ChainErrorFrom<$e> for $k
where
$k: Clone,
{
#[inline]
fn chain_error_from(
t: $e,
line_filename: Option<&'static str>,
) -> $crate::ChainError<$k> {
$crate::ChainError::new((*t.kind()).clone(), Some(Box::from(t)), line_filename)
}
}
impl std::error::Error for $e {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.0.source()