use std::fs;
use crate::log_file::LogFile;
pub struct Error {
err: Box<dyn std::error::Error>,
log_file: Option<LogFile>,
}
impl Error {
pub fn new(err: crate::error::Error, log_file: LogFile) -> Self {
Self {
err: Box::new(err),
log_file: Some(log_file),
}
}
}
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.err)?;
if let Some(source) = self.err.source() {
write!(f, "\n\nCaused by:")?;
let mut n: u32 = 0;
let mut error = Some(source);
while let Some(current_error) = error {
write!(f, "\n {}: {}", n, current_error)?;
n += 1;
error = current_error.source();
}
}
if let Some(log_file) = &self.log_file {
if let Ok(log_contents) = fs::read_to_string(&log_file.0) {
writeln!(f, "\n\nLog contents:")?;
writeln!(f, "{}", log_contents)?;
} else {
writeln!(f, "\n\nFailed to read log file: {:?}", log_file)?;
}
writeln!(f, "\nLog file: {:?}", log_file.0)?;
} else {
writeln!(f, "\n\nNo log file available.")?;
}
Ok(())
}
}
impl<E> From<E> for Error
where
E: std::error::Error + 'static,
{
fn from(error: E) -> Self {
Self {
err: Box::new(error),
log_file: None,
}
}
}
#[cfg(test)]
mod test {
use super::Error;
use std::fmt;
#[derive(Debug)]
struct SimpleError(&'static str);
impl fmt::Display for SimpleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::error::Error for SimpleError {}
#[derive(Debug)]
struct ChainedError {
msg: &'static str,
source: SimpleError,
}
impl fmt::Display for ChainedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.msg)
}
}
impl std::error::Error for ChainedError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.source)
}
}
#[test]
fn debug_format_without_source() {
let err: Error = SimpleError("something broke").into();
let output = format!("{err:?}");
assert!(output.contains("something broke"));
assert!(!output.contains("Caused by"));
assert!(output.contains("No log file available"));
}
#[test]
fn debug_format_with_source_chain() {
let err: Error = ChainedError {
msg: "outer error",
source: SimpleError("inner cause"),
}
.into();
let output = format!("{err:?}");
assert!(output.contains("outer error"));
assert!(output.contains("Caused by:"));
assert!(output.contains("0: inner cause"));
}
}