diff --git a/src/agdb/db/db_error.rs b/src/agdb/db/db_error.rs index f3eefcd9..b40e38b1 100644 --- a/src/agdb/db/db_error.rs +++ b/src/agdb/db/db_error.rs @@ -29,7 +29,15 @@ impl DbError { impl Display for DbError { fn fmt(&self, f: &mut Formatter<'_>) -> FMTResult { let location = self.source_location.to_string().replace('\\', "/"); - write!(f, "{} (at {})", self.description, location) + if let Some(cause) = &self.cause { + write!( + f, + "{} (at {})\ncaused by\n {}", + self.description, location, cause + ) + } else { + write!(f, "{} (at {})", self.description, location) + } } } @@ -111,23 +119,49 @@ mod tests { let file = file!(); let col__ = column!(); let line = line!(); - let error = DbError::from("file not found"); + let error = DbError::from("outer error"); assert_eq!( error.to_string(), format!( - "file not found (at {}:{}:{})", + "outer error (at {}:{}:{})", file.replace('\\', "/"), line + 1, col__ ) ); } + + #[test] + fn derived_from_display_cause() { + let file = file!(); + let column___ = column!(); + let line = line!(); + let mut error = DbError::from("outer error"); + let inner_column_adjusted = column!(); + let inner_line = line!(); + error.cause = Some(Box::new(DbError::from("inner error"))); + + assert_eq!( + error.to_string(), + format!( + "outer error (at {}:{}:{})\ncaused by\n inner error (at {}:{}:{})", + file.replace('\\', "/"), + line + 1, + column___, + file.replace('\\', "/"), + inner_line + 1, + inner_column_adjusted, + ) + ); + } + #[test] fn derived_from_partial_eq() { let left = DbError::from(IOError::from(ErrorKind::NotFound)); let right = DbError::from(IOError::from(ErrorKind::NotFound)); assert_eq!(left, right); } + #[test] fn derived_from_error() { let file = file!(); diff --git a/src/agdb/query/query_error.rs b/src/agdb/query/query_error.rs index a4dfee51..8c258c1e 100644 --- a/src/agdb/query/query_error.rs +++ b/src/agdb/query/query_error.rs @@ -1,4 +1,8 @@ use crate::db::db_error::DbError; +use std::error::Error; +use std::fmt::Display; +use std::fmt::Formatter; +use std::fmt::Result as FMTResult; use std::sync::PoisonError; /// Universal `query` error returned from all query operations. @@ -21,6 +25,26 @@ impl From for QueryError { } } +impl Display for QueryError { + fn fmt(&self, f: &mut Formatter<'_>) -> FMTResult { + if let Some(cause) = &self.cause { + write!(f, "{}\ncaused by\n {}", self.description, cause) + } else { + write!(f, "{}", self.description) + } + } +} + +impl Error for QueryError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + if let Some(cause) = &self.cause { + return Some(cause); + } + + None + } +} + impl From> for QueryError { fn from(value: PoisonError) -> Self { Self { @@ -49,6 +73,54 @@ impl From<&str> for QueryError { mod tests { use super::*; + #[test] + fn derived_from_display() { + let error = QueryError::from("outer error"); + assert_eq!(error.to_string(), format!("outer error")); + } + + #[test] + fn derived_from_display_cause() { + let mut error = QueryError::from("outer error"); + let file = file!(); + let inner_column = column!(); + let inner_line = line!(); + error.cause = Some(DbError::from("inner error")); + + assert_eq!( + error.to_string(), + format!( + "outer error\ncaused by\n inner error (at {}:{}:{})", + file.replace('\\', "/"), + inner_line + 1, + inner_column, + ) + ); + } + + #[test] + fn derived_from_error() { + let mut error = QueryError::from("outer error"); + let file = file!(); + let col_adjust_ = column!(); + let line = line!(); + let inner_error = DbError::from("inner error"); + + assert!(error.source().is_none()); + + error.cause = Some(inner_error); + + assert_eq!( + error.source().unwrap().to_string(), + format!( + "inner error (at {}:{}:{})", + file.replace('\\', "/"), + line + 1, + col_adjust_ + ) + ); + } + #[test] fn derived_from_debug_and_default() { format!("{:?}", QueryError::default());