use std::{error::Error, fmt, sync::Arc}; #[derive(Debug)] pub enum MTTError { Generic(Generic), } 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: &str) -> Self { Self { detail: detail.to_string(), source: None, } } fn add_source(&mut self, source: MTTError) { self.source = Some(Arc::new(source)); } } 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 generics { use super::*; #[test] fn new_error() { 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 error_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.into()); 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 ); } }