Skip to main content

g_err/report/
pretty_report.rs

1extern crate alloc;
2use alloc::format;
3use alloc::string::String;
4
5use super::Report;
6use crate::Config;
7use crate::gerr::Source;
8use crate::gerr_view::GErrView;
9use core::fmt::Write;
10use core::fmt::{Debug, Display};
11
12/// Report type for error reporting as pretty format.
13pub struct PrettyReport;
14
15impl Report for PrettyReport {
16    fn report<E, C: Config, D>(err: &E) -> String
17    where
18        for<'a> &'a E: Into<GErrView<'a, C, D>>,
19        C::Id: Display,
20        D: Debug,
21    {
22        let err = &err.into();
23        let mut out: String = String::new();
24        Self::header(&mut out);
25        Self::preamble::<C, D>(err, &mut out);
26        Self::data::<C, D>(err, &mut out);
27        Self::help::<C, D>(err, &mut out);
28        Self::tags::<C, D>(err, &mut out);
29        Self::location::<C, D>(err, &mut out);
30        Self::sources::<C, D>(err, &mut out);
31        #[cfg(feature = "backtrace")]
32        Self::backtrace::<C, D>(err, &mut out);
33
34        out
35    }
36}
37
38impl PrettyReport {
39    fn header(out: &mut String) {
40        let _ = writeln!(out, "Error Report");
41        let _ = writeln!(out, "============");
42    }
43    fn preamble<C: Config, D: Debug>(err: &GErrView<C, D>, out: &mut String)
44    where
45        C::Id: Display,
46    {
47        if let Some(id) = err.id {
48            let _ = writeln!(out, "ID: {}", id);
49        } else {
50            let _ = writeln!(out, "ID: -");
51        }
52        if let Some(code) = err.code {
53            let _ = writeln!(out, "Code: {code}");
54        } else {
55            let _ = writeln!(out, "Code: -");
56        }
57        let _ = writeln!(out, "Message: {}", err.message);
58    }
59    fn data<C: Config, D: Debug>(err: &GErrView<C, D>, out: &mut String) {
60        if let Some(data) = err.data {
61            let _ = writeln!(out, "Data:");
62            let pretty = format!("{data:#?}");
63
64            for line in pretty.lines() {
65                let _ = writeln!(out, "   {line}");
66            }
67        }
68    }
69    fn tags<C: Config, D: Debug>(err: &GErrView<C, D>, out: &mut String) {
70        if let Some(tags) = err.tags
71            && !tags.is_empty()
72        {
73            let _ = writeln!(out, "Tags:");
74            for tag in tags {
75                let _ = writeln!(out, "  - {tag}");
76            }
77        }
78    }
79    fn location<C: Config, D: Debug>(err: &GErrView<C, D>, out: &mut String) {
80        let _ = writeln!(
81            out,
82            "Location: {}:{}:{}",
83            err.location.file, err.location.line, err.location.column
84        );
85    }
86    fn sources<C: Config, D: Debug>(err: &GErrView<C, D>, out: &mut String) {
87        if let Some(sources) = err.sources {
88            let _ = writeln!(out, "Caused by:");
89            for (i, src) in sources.iter().enumerate() {
90                let i = i + 1;
91
92                match src {
93                    Source::Err(err) => {
94                        let _ = writeln!(out, "  {i}: {err}");
95                    }
96
97                    Source::GErr(gerr) => {
98                        let _ = writeln!(out, "  {i}: {}", gerr.message);
99
100                        if let Some(id) = gerr.id.as_ref() {
101                            let _ = writeln!(out, "     id: {}", id);
102                        } else {
103                            let _ = writeln!(out, "     id: -");
104                        }
105
106                        if let Some(code) = gerr.code.as_deref() {
107                            let _ = writeln!(out, "     code: {}", code);
108                        } else {
109                            let _ = writeln!(out, "     code: -");
110                        }
111
112                        if let Some(ref loc) = gerr.location {
113                            let _ =
114                                writeln!(out, "     at: {}:{}:{}", loc.file, loc.line, loc.column);
115                        }
116
117                        if let Some(tags) = &gerr.tags
118                            && !tags.is_empty()
119                        {
120                            let _ = writeln!(out, "     tags: {}", tags.join(", "));
121                        }
122
123                        if let Some(help) = &gerr.help {
124                            let _ = writeln!(out, "     help: {}", help);
125                        }
126
127                        if let Some(data) = &gerr.data {
128                            let _ = writeln!(out, "     data:");
129                            let pretty = format!("{data:#?}");
130
131                            for line in pretty.lines() {
132                                let _ = writeln!(out, "       {line}");
133                            }
134                        }
135
136                        if let Some(sources) = &gerr.sources {
137                            let _ = writeln!(out, "     caused by:");
138                            Self::write_sources(out, sources, 6);
139                        }
140                    }
141                }
142            }
143        }
144    }
145    fn write_sources(out: &mut String, sources: &[Source], indent: usize) {
146        let pad = " ".repeat(indent);
147
148        for src in sources {
149            match src {
150                Source::Err(err) => {
151                    let _ = writeln!(out, "{pad}- {err}");
152                }
153
154                Source::GErr(gerr) => {
155                    let _ = writeln!(out, "{pad}- {}", gerr.message);
156
157                    if let Some(id) = gerr.id.as_ref() {
158                        let _ = writeln!(out, "{pad}  id: {}", id);
159                    } else {
160                        let _ = writeln!(out, "{pad}  id: -");
161                    }
162
163                    if let Some(code) = gerr.code.as_deref() {
164                        let _ = writeln!(out, "{pad}  code: {}", code);
165                    } else {
166                        let _ = writeln!(out, "{pad}  code: -");
167                    }
168
169                    if let Some(ref loc) = gerr.location {
170                        let _ =
171                            writeln!(out, "{pad}  at: {}:{}:{}", loc.file, loc.line, loc.column);
172                    }
173
174                    if let Some(tags) = &gerr.tags
175                        && !tags.is_empty()
176                    {
177                        let _ = writeln!(out, "{pad}  tags: {}", tags.join(", "));
178                    }
179
180                    if let Some(help) = &gerr.help {
181                        let _ = writeln!(out, "{pad}  help: {help}");
182                    }
183
184                    if let Some(data) = &gerr.data {
185                        let _ = writeln!(out, "{pad}  data:");
186                        let pretty = format!("{data:#?}");
187                        for line in pretty.lines() {
188                            let _ = writeln!(out, "{pad}    {line}");
189                        }
190                    }
191
192                    if let Some(sources) = &gerr.sources {
193                        let _ = writeln!(out, "{pad}  caused by:");
194                        Self::write_sources(out, sources, indent + 3);
195                    }
196                }
197            }
198        }
199    }
200    fn help<C: Config, D: Debug>(err: &GErrView<C, D>, out: &mut String) {
201        if let Some(help) = err.help {
202            let _ = writeln!(out, "Help: {}", help);
203        }
204    }
205    #[cfg(feature = "backtrace")]
206    fn backtrace<C: Config, D: Debug>(err: &GErrView<C, D>, out: &mut String) {
207        let _ = writeln!(out, "Backtrace:");
208        let _ = writeln!(out, "{:#?}", err.backtrace);
209    }
210}