use async_std::path::PathBuf; use std::{error::Error, fmt}; #[derive(Debug)] pub enum ErrorCode { Undefined(String), // Read Write Errors CorruptFile, // Data Type Errors DataTypeIncorrect(String), // Entry Errors EntryExists(PathBuf), EntryWriteFailure(PathBuf), EntryReadFailure(PathBuf), EntryDeleteFailure(PathBuf), // Cache CacheReadWrite, } impl fmt::Display for ErrorCode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ErrorCode::Undefined(msg) => write!(f, "{}", msg), ErrorCode::DataTypeIncorrect(dtype) => write!(f, "data type '{}' is not valid", dtype), ErrorCode::CorruptFile => write!(f, "corrupt file"), ErrorCode::EntryExists(path) => write!( f, "entry '{}' already exists", path.file_name().unwrap().to_str().unwrap() ), ErrorCode::EntryWriteFailure(path) => write!( f, "entry '{}' write failure", path.file_name().unwrap().to_str().unwrap() ), ErrorCode::EntryReadFailure(path) => write!( f, "entry '{}' read failure", path.file_name().unwrap().to_str().unwrap() ), ErrorCode::EntryDeleteFailure(path) => write!( f, "entry '{}' delete failure", path.file_name().unwrap().to_str().unwrap() ), ErrorCode::CacheReadWrite => write!(f, "cache read write"), } } } #[derive(Debug)] pub struct DBError { pub code: ErrorCode, src: Option>, } impl DBError { pub fn new(msg: S) -> Self where S: Into, { Self { code: ErrorCode::Undefined(msg.into()), src: None, } } pub fn from_code(code: ErrorCode) -> Self { Self { code: code, src: None, } } pub fn add_source(&mut self, src: E) where E: Error + 'static, { self.src = Some(Box::new(src)); } } impl Error for DBError { fn source(&self) -> Option<&(dyn Error + 'static)> { match &self.src { Some(err) => Some(err.as_ref()), None => None, } } } impl fmt::Display for DBError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.code) } } #[cfg(test)] mod errors { use super::*; #[test] fn with_str() { let msg = "something happened"; let err = DBError::new(msg); assert!( err.to_string() == msg, "Got: {} -- Want: {}", err.to_string(), msg ); assert!( err.source().is_none(), "Error should initialize with no source." ); } #[test] fn with_string() { let msg = "it went boom".to_string(); let err = DBError::new(msg.clone()); assert!( err.to_string() == msg, "Got: {} -- Want: {}", err.to_string(), msg ); assert!( err.source().is_none(), "Error should initialize with no source." ); } #[test] fn using_error_code() { let msg = "utter failure"; let code = ErrorCode::Undefined(msg.to_string()); let err = DBError::from_code(code); assert_eq!(err.to_string(), msg); assert!(err.source().is_none(), "Should be no source"); } #[test] fn with_source() { let msg = "but this caused the problem"; let mut par = DBError::new("parent error"); let src = DBError::new(msg); par.add_source(src); let output = par.source(); assert!(output.is_some(), "Should return source."); let source = output.unwrap(); assert!(source.to_string() == msg); } } #[cfg(test)] mod codes { use super::*; use async_std::path::PathBuf; const items: [&str; 2] = ["first", "second"]; fn create_path_buffer() -> Vec { let mut output = Vec::new(); for item in items { let mut path = PathBuf::new(); path.push("thepath"); path.push(item); output.push(path); } output } #[test] fn undefined_display() { for item in items { let err = ErrorCode::Undefined(item.to_string()); assert_eq!(err.to_string(), item); } } #[test] fn incorrect_data_type() { for item in items { let err = ErrorCode::DataTypeIncorrect(item.to_string()); assert_eq!( err.to_string(), format!("data type '{}' is not valid", item) ); } } #[test] fn corrupt_file() { assert_eq!(ErrorCode::CorruptFile.to_string(), "corrupt file"); } #[test] fn entry_exists() { for path in create_path_buffer() { let err = ErrorCode::EntryExists(path.clone()); assert_eq!( err.to_string(), format!( "entry '{}' already exists", path.file_name().unwrap().to_str().unwrap() ) ); } } #[test] fn entry_write_failure() { for path in create_path_buffer() { let err = ErrorCode::EntryWriteFailure(path.clone()); assert_eq!( err.to_string(), format!( "entry '{}' write failure", path.file_name().unwrap().to_str().unwrap() ) ); } } #[test] fn entry_read_failure() { for path in create_path_buffer() { let err = ErrorCode::EntryReadFailure(path.clone()); assert_eq!( err.to_string(), format!( "entry '{}' read failure", path.file_name().unwrap().to_str().unwrap() ) ); } } #[test] fn entry_delete_failure() { for path in create_path_buffer() { let err = ErrorCode::EntryDeleteFailure(path.clone()); assert_eq!( err.to_string(), format!( "entry '{}' delete failure", path.file_name().unwrap().to_str().unwrap() ) ); } } #[test] fn cache_read_write_failure() { let err = ErrorCode::CacheReadWrite; assert_eq!(err.to_string(), "cache read write"); } }