1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
//! [Cause] is a tiny generic implementation of the [std::error::Error] trait.
//!
//! It takes 1 type parameter(`T: Debug`) who describes what error type happened.
//!
//! It is dereferencable as `&T`.
//!
//! And if you use macro [cause], it automatically stores some extra information,
//! the filename and line number, only when it was compiled with `debug_assertions`.
//!
//! # Examples
//!
//! ```
//! #[derive(Debug, PartialEq, Eq)]
//! pub enum ErrorType {
//! InvalidArgumentsError,
//! InternalError,
//! NotFoundError,
//! }
//!
//! use ErrorType::*;
//! use cause::Cause;
//! use std::error::Error;
//!
//! // It creates an instance of `Cause<ErrorType>`
//! let cause = Cause::new(InternalError);
//! assert_eq!(cause.to_string(), "InternalError".to_string());
//! assert!(cause.message().is_none());
//! assert!(cause.source().is_none());
//!
//! // It is dereferencable.
//! assert_eq!(*cause, InternalError);
//!
//! let http_status_code = match *cause {
//! InternalError => 500,
//! InvalidArgumentsError => 400,
//! NotFoundError => 404,
//! };
//! assert_eq!(http_status_code, 500);
//!
//! // set the message:
//! let cause = Cause::new(InvalidArgumentsError).msg("oops!");
//! assert_eq!(cause.to_string(), "InvalidArgumentsError: oops!".to_string());
//! assert_eq!(cause.message(), Some(&"oops!".to_string()));
//! assert!(cause.source().is_none());
//!
//! // set the source of this error (any error type can be set with `src()`):
//! let cause = Cause::new(InternalError).src(Cause::new(NotFoundError));
//! assert_eq!(
//! cause.to_string(),
//! "InternalError\n\nCaused by:\n NotFoundError\n".to_string()
//! );
//! assert!(cause.message().is_none());
//! assert!(cause.source().is_some());
//!
//! // an example of Cause who have a standard io error.
//! use std::io::Error as IoErr;
//! use std::io::ErrorKind;
//! let io_err = IoErr::new(ErrorKind::Other, "oh no!");
//! println!("{}", Cause::new(InternalError).src(io_err).msg("internal error caused by io error"));
//!
//! // a couple of macro examples
//! use cause::cause;
//!
//! let cause = cause!(InternalError);
//! println!("{}", cause);
//! // => "InternalError" on release build
//! // => "InternalError: [lib.rs:64]" on debug build
//!
//! let cause = cause!(NotFoundError, "There is no such contents.");
//! println!("{}", cause);
//! // => "InternalError: There is no such contents." on release build
//! // => "InternalError: There is no such contents. [lib.rs:69]" on debug build
//!
//! ```
/// A macro to create a [Cause] which situationally appends filename and line number information at the end of message.
#[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;
/// A tiny generic implementation of the [std::error::Error] trait.
#[derive(Debug)]
pub struct Cause<T> {
cause: T,
msg: Option<String>,
src: Option<Box<dyn Error + Send + 'static>>,
}
impl<T> Cause<T> {
/// Create a [Cause] instance with its `cause`.
pub fn new(cause: T) -> Self {
Self {
cause,
msg: None,
src: None,
}
}
/// Set the message for this error.
pub fn msg(mut self, msg: impl Into<String>) -> Self {
self.msg = Some(msg.into());
self
}
/// Set the lower-level source of this error, if any.
pub fn src(mut self, src: impl Error + Send + 'static) -> Self {
self.src = Some(Box::new(src));
self
}
/// Get a reference to the `cause`
pub fn cause(&self) -> &T {
&self.cause
}
/// Get a reference to the message
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"));
}
}