use std::{error::Error, fmt, sync::Arc}; #[derive(Debug)] pub enum MTTError { Generic(Generic), } impl MTTError { pub fn new(detail: S) -> Self where S: Into, { Generic::new(detail).into() } pub fn add_source(&mut self, source: E) where E: Into, { match self { MTTError::Generic(err) => err.add_source(source), } } } impl Error for MTTError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { MTTError::Generic(err) => err.source(), } } } impl fmt::Display for MTTError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { MTTError::Generic(err) => write!(f, "{}", err), } } } impl From for MTTError { fn from(err: Generic) -> Self { MTTError::Generic(err) } } #[derive(Debug)] pub struct Generic { detail: String, source: Option>, } impl Generic { fn new(detail: S) -> Self where S: Into, { Self { detail: detail.into(), source: None, } } fn add_source(&mut self, source: E) where E: Into, { self.source = Some(Arc::new(source.into())); } } impl Error for Generic { fn source(&self) -> Option<&(dyn Error + 'static)> { match &self.source { Some(err) => Some(err.as_ref()), None => None, } } } impl fmt::Display for Generic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.detail) } } #[cfg(test)] mod mtterror { use super::*; #[test] fn create_with_str() { let detail = "Something"; let err = MTTError::new(detail); assert!( err.to_string() == detail, "\n\nGot: {}\nWant: {}\n\n", err.to_string(), detail ); assert!( err.source().is_none(), "Error source should initialoze to None." ); } #[test] fn create_with_string() { let detail = "massive".to_string(); let err = MTTError::new(detail.clone()); assert!( err.to_string() == detail, "\n\nGot: {}\nWant: {}\n\n", err.to_string(), detail ); } #[test] fn with_source() { let mut err = MTTError::new("the error"); let detail = "This is the cause"; let src = MTTError::new(detail); err.add_source(src); assert!( err.source().unwrap().to_string() == detail, "/n/nGot: {}\nWant: {}\n\n", err.source().unwrap().to_string(), detail ); } } #[cfg(test)] mod generic { use super::*; #[test] fn create_with_str() { let detail = "new error"; let err = Generic::new(detail); assert!( err.to_string() == detail, "\n\nGot: {}\nWant: {}\n\n", err.to_string(), detail ); assert!( err.source().is_none(), "Error source should initialoze to None." ); let error: MTTError = err.into(); assert!( error.to_string() == detail, "\n\nGot: {}\nWant: {}\n\n", error.to_string(), detail ); } #[test] fn create_with_string() { let detail = "some error".to_string(); let err = Generic::new(detail.clone()); assert!( err.to_string() == detail, "\n\nGot: {}\nWant: {}\n\n", err.to_string(), detail ); } #[test] fn with_source() { let par_detail = "parent error"; let cld_detail = "child error"; let par_err = Generic::new(par_detail); let mut cld_err = Generic::new(cld_detail); cld_err.add_source(par_err); assert!( cld_err.source().unwrap().to_string() == par_detail, "/n/nGot: {}\nWant: {}\n\n", cld_err.source().unwrap().to_string(), par_detail ); let error: MTTError = cld_err.into(); assert!( error.source().unwrap().to_string() == par_detail, "/n/nGot: {}\nWant: {}\n\n", error.source().unwrap().to_string(), par_detail ); } }