ts_error/
report.rs

1//! Display an error stack by traversing their source.
2
3use alloc::boxed::Box;
4use core::{error::Error, fmt};
5
6use ts_ansi::ansi::{BOLD, DEFAULT, RED, RESET};
7
8/// Trait for converting something into an error report.
9pub trait IntoReport<T> {
10    /// Convert self into an error report if self is an error.
11    fn into_report(self) -> Result<T, Report<'static>>;
12}
13
14impl<T, E: Error + 'static> IntoReport<T> for Result<T, E> {
15    fn into_report(self) -> Result<T, Report<'static>> {
16        self.map_err(|source| Report::new(source))
17    }
18}
19
20/// An error report, displays the error stack of some error.
21pub struct Report<'e> {
22    /// The error for this report.
23    pub source: Box<dyn Error + 'e>,
24}
25impl<'e> Report<'e> {
26    /// Create a new error report.
27    pub fn new<E: Error + 'e>(source: E) -> Self {
28        Self {
29            source: Box::new(source),
30        }
31    }
32}
33impl Error for Report<'static> {
34    fn source(&self) -> Option<&(dyn Error + 'static)> {
35        Some(self.source.as_ref())
36    }
37}
38impl fmt::Debug for Report<'_> {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        write!(f, "{self}")
41    }
42}
43impl fmt::Display for Report<'_> {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        let mut current_error = Some(self.source.as_ref());
46        let mut count = 1;
47
48        while let Some(error) = current_error {
49            writeln!(f, " {BOLD}{RED}{count}{DEFAULT}.{RESET} {error}")?;
50
51            count += 1;
52            current_error = error.source();
53        }
54
55        Ok(())
56    }
57}