1use std::collections::BTreeMap;
2use std::fmt;
3
4use crate::compiler::tokens::Span;
5use crate::error::ErrorKind;
6use crate::value::Value;
7
8#[cfg_attr(docsrs, doc(cfg(feature = "debug")))]
10#[derive(Default)]
11pub(crate) struct DebugInfo {
12 pub(crate) template_source: Option<String>,
13 pub(crate) referenced_locals: BTreeMap<String, Value>,
14}
15
16struct VarPrinter<'x>(&'x BTreeMap<String, Value>);
17
18impl fmt::Debug for VarPrinter<'_> {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 if self.0.is_empty() {
21 return f.write_str("No referenced variables");
22 }
23 let mut m = f.debug_struct("Referenced variables:");
24 let mut vars = self.0.iter().collect::<Vec<_>>();
25 vars.sort_by_key(|x| x.0);
26 for (key, value) in vars {
27 m.field(key, value);
28 }
29 m.finish()
30 }
31}
32
33impl DebugInfo {
34 pub fn source(&self) -> Option<&str> {
35 self.template_source.as_deref()
36 }
37}
38
39pub(super) fn render_debug_info(
40 f: &mut fmt::Formatter,
41 name: Option<&str>,
42 kind: ErrorKind,
43 line: Option<usize>,
44 span: Option<Span>,
45 info: &DebugInfo,
46) -> fmt::Result {
47 if let Some(source) = info.source() {
48 let title = format!(
49 " {} ",
50 name.unwrap_or_default()
51 .rsplit(&['/', '\\'])
52 .next()
53 .unwrap_or("Template Source")
54 );
55 ok!(writeln!(f));
56 writeln!(f, "{:-^1$}", title, 79).unwrap();
57 let lines: Vec<_> = source.lines().enumerate().collect();
58 let idx = line.unwrap_or(1).saturating_sub(1);
59 let skip = idx.saturating_sub(3);
60 let pre = lines.iter().skip(skip).take(3.min(idx)).collect::<Vec<_>>();
61 let post = lines.iter().skip(idx + 1).take(3).collect::<Vec<_>>();
62 for (idx, line) in pre {
63 writeln!(f, "{:>4} | {}", idx + 1, line).unwrap();
64 }
65
66 if let Some(line) = lines.get(idx) {
67 writeln!(f, "{:>4} > {}", idx + 1, line.1).unwrap();
68 }
69 if let Some(span) = span {
70 if span.start_line == span.end_line {
71 ok!(writeln!(
72 f,
73 " i {}{} {}",
74 " ".repeat(span.start_col as usize),
75 "^".repeat(span.end_col as usize - span.start_col as usize),
76 kind,
77 ));
78 }
79 }
80
81 for (idx, line) in post {
82 writeln!(f, "{:>4} | {}", idx + 1, line).unwrap();
83 }
84 write!(f, "{:~^1$}", "", 79).unwrap();
85 }
86 ok!(writeln!(f));
87 ok!(writeln!(f, "{:#?}", VarPrinter(&info.referenced_locals)));
88 write!(f, "{:-^1$}", "", 79).unwrap();
89 Ok(())
90}