ts_error/
report.rs

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