Skip to main content

g_err/report/
markdown_report.rs

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
10/// Report type for error reporting as markdown.
11pub 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}