Skip to main content

g_err/report/
trace_report.rs

1extern crate alloc;
2use alloc::format;
3use alloc::string::String;
4use alloc::vec::Vec;
5
6use super::Report;
7use crate::gerr::{Config, Source};
8use crate::gerr_view::GErrView;
9use core::fmt::{Debug, Display, Write};
10
11/// Report type for error reporting as tracing format.
12pub struct TraceReport;
13
14impl Report for TraceReport {
15    fn report<E, C: Config, D>(err: &E) -> String
16    where
17        for<'a> &'a E: Into<GErrView<'a, C, D>>,
18        C::Id: Display,
19        D: Debug,
20    {
21        let err = &err.into();
22        let mut out: String = String::new();
23        Self::header(err, &mut out);
24        Self::sources(err, &mut out);
25
26        out
27    }
28}
29
30impl TraceReport {
31    fn header<C: Config, D: Debug>(err: &GErrView<C, D>, out: &mut String)
32    where
33        C::Id: Display,
34    {
35        match (err.id, err.code) {
36            (Some(id), Some(code)) => {
37                let _ = writeln!(out, "[{id}][{code}] {}", err.message);
38            }
39            (Some(id), None) => {
40                let _ = writeln!(out, "[{id}][-] {}", err.message);
41            }
42            (None, Some(code)) => {
43                let _ = writeln!(out, "[-][{code}] {}", err.message);
44            }
45            (None, None) => {
46                let _ = writeln!(out, "[-][-] {}", err.message);
47            }
48        }
49    }
50
51    fn sources<C: Config, D: Debug>(err: &GErrView<C, D>, out: &mut String) {
52        if let Some(sources) = err.sources {
53            Self::write_sources(sources, out, &mut Vec::new());
54        }
55    }
56
57    fn write_sources(
58        sources: &[Source],
59        out: &mut String,
60        ancestors: &mut Vec<bool>, // true = ancestor was last child
61    ) {
62        for (idx, src) in sources.iter().enumerate() {
63            let is_last = idx + 1 == sources.len();
64
65            // Draw ancestor guide lines.
66            for &ancestor_is_last in ancestors.iter() {
67                let _ = write!(out, "{}", if ancestor_is_last { "   " } else { "│  " });
68            }
69
70            let branch = if is_last { "└─ " } else { "├─ " };
71
72            match src {
73                Source::Err(e) => {
74                    let _ = writeln!(out, "{branch}{e}");
75                }
76
77                Source::GErr(ge) => {
78                    let msg = match (ge.id.as_ref(), ge.code.as_ref()) {
79                        (Some(id), Some(code)) => {
80                            format!("[{id}][{code}] {}", ge.message)
81                        }
82                        (Some(id), None) => {
83                            format!("[{id}][-] {}", ge.message)
84                        }
85                        (None, Some(code)) => {
86                            format!("[-][{code}] {}", ge.message)
87                        }
88                        (None, None) => {
89                            format!("[-][-] {}", ge.message)
90                        }
91                    };
92
93                    let _ = writeln!(out, "{branch}{msg}");
94
95                    if let Some(nested) = &ge.sources {
96                        ancestors.push(is_last);
97                        Self::write_sources(nested, out, ancestors);
98                        ancestors.pop();
99                    }
100                }
101            }
102        }
103    }
104}