From 878c58b49eeecb1e2a1f61bd487e2e558766f137 Mon Sep 17 00:00:00 2001 From: Avril Date: Sat, 29 Aug 2020 13:10:54 +0100 Subject: [PATCH] improvement --- src/error/mod.rs | 82 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 12 deletions(-) diff --git a/src/error/mod.rs b/src/error/mod.rs index 9f4bde7..b5c0dec 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -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) where D: fmt::Display; impl fmt::Debug for DisplayError @@ -147,14 +184,26 @@ impl error::Error for Error impl error::Error for DisplayError 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(()) + } }