#[macro_export]
macro_rules! cause {
($type:expr) => {
if cfg!(debug_assertions) {
cause::Cause::new($type).msg(format!("[{}:{}]", file!(), line!()))
} else {
cause::Cause::new($type)
}
};
($type:expr, $msg:expr) => {
if cfg!(debug_assertions) {
cause::Cause::new($type).msg(format!("{} [{}:{}]", $msg, file!(), line!()))
} else {
cause::Cause::new($type).msg($msg)
}
};
}
use std::error::Error;
#[derive(Debug)]
pub struct Cause<T> {
cause: T,
msg: Option<String>,
src: Option<Box<dyn Error + Send + 'static>>,
}
impl<T> Cause<T> {
pub fn new(cause: T) -> Self {
Self {
cause,
msg: None,
src: None,
}
}
pub fn msg(mut self, msg: impl Into<String>) -> Self {
self.msg = Some(msg.into());
self
}
pub fn src(mut self, src: impl Error + Send + 'static) -> Self {
self.src = Some(Box::new(src));
self
}
pub fn cause(&self) -> &T {
&self.cause
}
pub fn message(&self) -> Option<&String> {
match self.msg.as_ref() {
Some(msg) => Some(msg),
None => None,
}
}
}
use std::fmt::Display;
use std::fmt::Debug;
impl<T: Debug> Display for Cause<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let mut message: String = match self.msg.as_ref() {
Some(m) => format!("{:?}: {}", self.cause, m),
None => format!("{:?}", self.cause),
};
if let Some(ref s) = self.src {
message.push_str(&format!("\n\nCaused by:\n {}\n", s));
}
write!(f, "{}", message)
}
}
impl<T: Debug> Error for Cause<T> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match &self.src {
Some(e) => Some(e.as_ref()),
None => None,
}
}
}
use std::ops::Deref;
impl<T: Debug> Deref for Cause<T> {
type Target = T;
fn deref(&self) -> &T {
&self.cause
}
}
#[cfg(test)]
mod tests {
#[derive(Debug, PartialEq, Eq)]
pub enum ErrorType {
InvalidArgumentsError,
InternalError,
UnknownError,
}
#[derive(Debug)]
enum AlphabetError {
AError,
BError,
CError,
}
#[test]
fn it_works() {
use ErrorType::*;
use AlphabetError::*;
use super::Cause;
let cause = Cause::new(InternalError);
let http_status_code = match *cause {
InternalError => 500,
InvalidArgumentsError => 400,
_ => 418
};
assert_eq!(*cause, InternalError);
assert_eq!(http_status_code, 500);
println!("{}", Cause::new(InternalError).msg("oh no!"));
println!("{}", Cause::new(InvalidArgumentsError).msg("oops"));
println!("{}", Cause::new(AError));
println!("{}", Cause::new(BError));
println!("{}", Cause::new(InternalError).src(Cause::new(UnknownError).msg("nested")).msg("something went wrong"));
println!("{}", Cause::new(InternalError).src(Cause::new(CError)).msg( "another nested"));
use std::io::{Error, ErrorKind};
let io_err = Error::new(ErrorKind::Other, "oh no!");
println!("{}", Cause::new(InternalError).src(io_err).msg("internal error caused by io error"));
}
}