use snafu::Snafu;
use std::fmt::{Debug, Display};
#[derive(Debug, Snafu)]
#[snafu(display("{}", msg))]
pub struct Error {
msg: String,
backtrace: snafu::Backtrace,
}
impl Error {
pub fn new(msg: &str) -> Self {
Error {
msg: msg.to_string(),
backtrace: snafu::Backtrace::capture(),
}
}
pub fn to_string(self, debug: bool) -> String {
if debug {
format!(">> {}\n\nBacktrace:\n{}", self.msg, self.backtrace)
} else {
self.msg
}
}
pub fn chain(self, msg: &str) -> Self {
Error {
msg: format!("{}\n>> {}", msg, self.msg),
backtrace: self.backtrace,
}
}
pub fn to_res<T>(self) -> Result<T, Self> {
Err(self)
}
}
pub fn map<S>(err: S, msg: &str) -> Error
where
S: Display,
{
Error::new(&err.to_string()).chain(msg)
}
pub fn map_err<T, S>(res: Result<T, S>, msg: &str) -> Result<T, Error>
where
S: Display,
{
res.map_err(|s| map(s, msg))
}
pub fn map_err_debug<T, S>(res: Result<T, S>, msg: &str) -> Result<T, Error>
where
S: Debug,
{
res.map_err(|s| Error::new(&format!("{}\n>> {:?}", msg, s)))
}
pub fn ok_or_else<T>(opt: Option<T>, msg: &str) -> Result<T, Error> {
opt.ok_or_else(|| Error::new(msg))
}
pub fn res_to_string<T>(res: Result<T, Error>, debug: bool) -> String {
match res {
Ok(_) => "Program finished successfully.".to_string(),
Err(e) => format!("Program terminated with an error:\n{}", e.to_string(debug)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Error as IoError;
#[test]
fn to_string_test() {
let msg = "Test error";
let msg2 = "Another test error";
assert!(Error::new(msg).chain(msg2).to_string(true).contains(&msg));
assert!(Error::new(msg).chain(msg2).to_string(true).contains(&msg2));
assert!(Error::new(msg).chain(msg2).to_string(false).contains(&msg));
assert!(Error::new(msg).chain(msg2).to_string(false).contains(&msg2));
assert!(map(Error::new(msg), msg2).to_string(true).contains(&msg));
assert!(map(Error::new(msg), msg2).to_string(true).contains(&msg2));
assert!(map(Error::new(msg), msg2).to_string(false).contains(&msg));
assert!(map(Error::new(msg), msg2).to_string(false).contains(&msg2));
}
#[test]
fn chain_test() {
let msg1 = "Test error 1";
let msg2 = "Test error 2";
let msg3 = "Test error 3";
let e = Error::new(msg1).chain(&msg2).chain(&msg3);
assert!(e.msg.contains(&msg1));
assert!(e.msg.contains(&msg2));
assert!(e.msg.contains(&msg3));
}
#[test]
fn to_res_test() {
let e = Error::new("Test error");
assert!(e.to_res::<()>().is_err());
}
#[test]
fn map_test() {
let err = IoError::new(std::io::ErrorKind::Other, "Test error");
let err_msg = err.to_string();
let new_msg = "Test error";
let new_msg2 = "Test error 2";
let new_err = map(map(err, &new_msg), &new_msg2);
assert!(new_err.msg.contains(&err_msg));
assert!(new_err.msg.contains(new_msg));
assert!(new_err.msg.contains(&new_msg2));
}
#[test]
fn map_err_test() {
let err = IoError::new(std::io::ErrorKind::Other, "Test error");
let err_msg = err.to_string();
let new_msg: String = "Test error".to_string();
let new_msg2: String = "Test error 2".to_string();
let new_res: Result<(), Error> = map_err(map_err(Err(err), &new_msg), &new_msg2);
assert!(new_res.is_err());
let new_err = new_res.err().unwrap();
assert!(new_err.msg.contains(&err_msg));
assert!(new_err.msg.contains(&new_msg));
assert!(new_err.msg.contains(&new_msg2));
let res: Result<(), IoError> = Ok(());
let new_res: Result<(), Error> = map_err(res, &new_msg);
assert!(new_res.is_ok());
assert_eq!(new_res.unwrap(), ());
}
#[test]
fn map_err_debug_test() {
let err: Vec<usize> = Vec::new();
let err_msg = format!("{:?}", err);
let new_msg = "Test error";
let new_res: Result<(), Error> = map_err_debug(Err(err), new_msg);
assert!(new_res.is_err());
let new_err = new_res.err().unwrap();
assert!(new_err.msg.contains(&err_msg));
assert!(new_err.msg.contains(&new_msg));
let res: Result<(), Vec<usize>> = Ok(());
let new_res: Result<(), Error> = map_err_debug(res, new_msg);
assert!(new_res.is_ok());
assert_eq!(new_res.unwrap(), ());
}
#[test]
fn ok_or_else_test() {
let opt: Option<usize> = Some(0);
let msg = "Test error";
let res: Result<usize, Error> = ok_or_else(opt, msg);
assert!(res.is_ok());
assert_eq!(res.unwrap(), 0);
let opt: Option<usize> = None;
let res: Result<usize, Error> = ok_or_else(opt, msg);
assert!(res.is_err());
let err = res.err().unwrap();
assert!(err.msg.contains(&msg));
}
#[test]
fn res_to_string_test() {
let msg = "Test error";
let msg2 = "Another test error";
assert!(res_to_string(map_err(Error::new(msg).to_res::<()>(), &msg2), true).contains(msg));
assert!(res_to_string(map_err(Error::new(msg).to_res::<()>(), &msg2), true).contains(msg2));
assert!(res_to_string(map_err(Error::new(msg).to_res::<()>(), &msg2), false).contains(msg));
assert!(
res_to_string(map_err(Error::new(msg).to_res::<()>(), &msg2), false).contains(msg2)
);
}
}