@ -38,6 +38,7 @@ impl Default for ErrorKind
impl Error
{
/// Into the `ErrorKind`
pub fn into_inner ( self ) -> ErrorKind
{
* self . internal
@ -61,7 +62,6 @@ impl Error
& self . context
}
/// Create a new error with a constant string
pub fn new_static ( err : ErrorKind , ctx : Context , message : & ' static str ) -> Self
{
@ -89,6 +89,15 @@ impl Error
message : Message ::from_other ( message ) ,
}
}
/// Create a new error with no message
pub fn new_none ( err : ErrorKind , ctx : Context ) -> Self
{
Self {
internal : box err ,
context : ctx ,
message : Message ::None ,
}
}
}
impl fmt ::Display for Error
@ -122,10 +131,38 @@ impl error::Error for Error
( $fmt :literal $( $tt :tt ) * ) = > ( $crate ::context ! ( format! ( $fmt $( $tt ) * ) ) ) ;
}
/// Macro for instantiating errors and also ad-hoc error types
///
/// # Instantiating `Error`.
/// The primary use is to return errors of the `Error` struct, this is how it can work.
/// ## With `ErrorKind`
/// The kind should be prefixed with `;`:
/// ```
/// error!(ErrorKind::Unknown; "unknown error message");
/// error!(ErrorKind::IO(yield io_err; "I/O error opening {:?}", file));
/// ```
///
/// ## Without `ErrorKind`
/// With all except a single string literal, the message is boxed as the `ErrorKind`, and printed with a `None` message.
/// This means that the message given as an ad-hoc error type is counted as the source.
///
/// ```
/// error!("unknown error");
/// error!(yield "something went wrong: {}", message);
/// ```
///
/// # Defining errors
/// To create ad-hoc errors, use `error!(impl ...)`. It has the same syntax as the above cases, except with no error kind
/// ```
/// error!(impl "Error message");
/// error!(impl yield "lazy formatted string {}", string);
/// ```
///
/// # `yield`ing formatting
/// When format string are provided to `error!()`, they by default will use the eager `format!()` macro, which will immediately evaluate it's arguments and produce the string to put into the error.
/// To instead box the arguments and evaluate as-needed, we can use the `lazy_format` crate by prefixing the format string literals with `yield`. This works on both `Error` instantiations and ad-hoc error definitions.
#[ macro_export ] macro_rules! error {
( $kind :expr , $lit :literal ) = > ( $crate ::error ::Error ::new_static ( $kind , $crate ::context ! ( ) , $lit ) ) ;
( $lit :literal ) = > ( $crate ::error ! ( Default ::default ( ) , $lit ) ) ;
( $kind :expr , $msg :expr ) = > {
( @ $msg :expr ) = > {
{
pub struct DisplayError < D > ( D ) where D : fmt ::Display ;
impl < D > fmt ::Debug for DisplayError < D >
@ -147,14 +184,26 @@ impl error::Error for Error
impl < D > error ::Error for DisplayError < D >
where D : fmt ::Display { }
$crate ::error ::Error ::new ( $kind , $crate ::context ! ( ) , DisplayError ( $msg ) )
DisplayError ( $msg )
}
} ;
( $msg :expr ) = > ( $crate ::error ! ( Default ::default ( ) , $msg ) ) ;
( yield $kind :expr , $lit :literal $( $tt :tt ) * ) = > ( $crate ::error ! ( $kind , lazy_format ::lazy_format ! ( $lit $( $tt ) * ) ) ) ;
( $kind :expr , $lit :literal $( $tt :tt ) * ) = > ( $crate ::error ! ( $kind , format! ( $lit $( $tt ) * ) ) ) ;
( yield $lit :literal $( $tt :tt ) * ) = > ( $crate ::error ! ( yield Default ::default ( ) , $lit $( $tt ) * ) ) ;
( $lit :literal $( $tt :tt ) * ) = > ( $crate ::error ! ( Default ::default ( ) , $lit $( $tt ) * ) ) ;
( impl $lit :literal ) = > ( $crate ::error ! ( @ $lit ) ) ;
( impl $expr :expr ) = > ( $crate ::error ! ( @ $expr ) ) ;
( impl yield $lit :literal $( $tt :tt ) * ) = > ( $crate ::error ! ( @ lazy_format ::lazy_format ! ( $lit $( $tt ) * ) ) ) ;
( impl $lit :literal $( $tt :tt ) * ) = > ( $crate ::error ! ( @ format! ( $lit $( $tt ) * ) ) ) ;
( $kind :expr ; $lit :literal ) = > ( $crate ::error ::Error ::new_static ( $kind , $crate ::context ! ( ) , $lit ) ) ;
( $lit :literal ) = > ( $crate ::error ! ( Default ::default ( ) , $lit ) ) ;
( $kind :expr ; $msg :expr ) = > ( $crate ::error ::Error ::new ( $kind , $crate ::context ! ( ) , $msg ) ) ;
( $msg :expr ) = > ( $crate ::error ::Error ::new_none ( $crate ::error ::ErrorKind ::Any ( box $crate ::error ! ( @ $msg ) ) , $crate ::context ! ( ) ) ) ;
( yield $lit :literal $( $tt :tt ) * ) = > ( $crate ::error ::Error ::new_none ( $crate ::error ::ErrorKind ::Any ( box $crate ::error ! ( @ lazy_format ::lazy_format ! ( $lit $( $tt ) * ) ) ) , $crate ::context ! ( ) ) ) ;
( $lit :literal $( $tt :tt ) * ) = > ( $crate ::error ::Error ::new_none ( $crate ::error ::ErrorKind ::Any ( box $crate ::error ! ( @ format! ( $lit $( $tt ) * ) ) ) , $crate ::context ! ( ) ) ) ;
( yield $kind :expr ; $lit :literal $( $tt :tt ) * ) = > ( $crate ::error ! ( $kind ; lazy_format ::lazy_format ! ( $lit $( $tt ) * ) ) ) ;
( $kind :expr ; $lit :literal $( $tt :tt ) * ) = > ( $crate ::error ::Error ::new_string ( $kind , $crate ::context ! ( ) , format! ( $lit $( $tt ) * ) ) ) ;
}
#[ derive(Debug) ]
@ -185,6 +234,7 @@ impl fmt::Display for ErrorKind
match self {
Self ::IO ( io ) = > write! ( f , "i/o error: {}" , io ) ,
Self ::Wrap ( wrap ) = > write! ( f , "{}" , wrap ) ,
Self ::Any ( any ) = > write! ( f , "{}" , any ) ,
Self ::None = > Ok ( ( ) ) ,
_ = > write! ( f , "unknown error" ) ,
}
@ -198,6 +248,7 @@ impl error::Error for ErrorKind
Some ( match & self {
Self ::IO ( io ) = > io ,
Self ::Wrap ( wrap ) = > return wrap . source ( ) ,
Self ::Any ( any ) = > any . as_ref ( ) ,
_ = > return None ,
} )
}
@ -239,9 +290,8 @@ mod test
#[ test ]
fn test ( ) -> ReportedResult < ( ) >
{
color_eyre ::install ( ) ? ;
fn testt ( ) -> Result < ( ) > {
let err = error ! ( yield one ( ) . unwrap_err ( ) . into_inner ( ) , "hello world {} {}" , 1 , 2 ) ;
let err = error ! ( yield one ( ) . unwrap_err ( ) . into_inner ( ) ; "hello world {} {}" , 1 , 2 ) ;
Err ( err )
. wrap_err_with ( | | context ! ( "with string" ) )
. wrap_err_with ( | | context ! ( yield "with another string and some stuffs {}" , 1 ) ) ? ;
@ -275,8 +325,16 @@ mod test
Ok ( one ( )
. aggregate_with ( | _ | testt ( ) )
. aggregate_with ( | _ | two ( ) )
. aggregate_with ( | _ | works ( ) )
. aggregate_with ( | _ | three ( ) ) ? )
}
#[ test ]
fn strings ( ) -> ReportedResult < ( ) >
{
Err ( error ! ( yield "literal format {}" , 1 ) ) ? ;
Ok ( ( ) )
}
}