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