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
12pub 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}