1use super::GentError;
4
5pub struct ErrorReporter<'a> {
7 source: &'a str,
8 filename: &'a str,
9 pub use_colors: bool,
10}
11
12impl<'a> ErrorReporter<'a> {
13 pub fn new(source: &'a str, filename: &'a str) -> Self {
15 Self {
16 source,
17 filename,
18 use_colors: atty::is(atty::Stream::Stderr),
19 }
20 }
21
22 fn line_col(&self, offset: usize) -> (usize, usize) {
24 let mut line = 1;
25 let mut col = 1;
26 for (i, ch) in self.source.char_indices() {
27 if i >= offset {
28 break;
29 }
30 if ch == '\n' {
31 line += 1;
32 col = 1;
33 } else {
34 col += 1;
35 }
36 }
37 (line, col)
38 }
39
40 fn get_line(&self, offset: usize) -> &str {
42 let start = self.source[..offset]
43 .rfind('\n')
44 .map(|i| i + 1)
45 .unwrap_or(0);
46 let end = self.source[offset..]
47 .find('\n')
48 .map(|i| offset + i)
49 .unwrap_or(self.source.len());
50 &self.source[start..end]
51 }
52
53 pub fn format(&self, error: &GentError) -> String {
55 let mut output = String::new();
56
57 let error_msg = error.to_string();
59 if self.use_colors {
60 output.push_str(&format!("\x1b[31merror:\x1b[0m {}\n", error_msg));
61 } else {
62 output.push_str(&format!("error: {}\n", error_msg));
63 }
64
65 if let Some(span) = error.span() {
67 let (line, col) = self.line_col(span.start);
68 let source_line = self.get_line(span.start);
69 let caret_count = (span.end - span.start).max(1);
70
71 if self.use_colors {
73 output.push_str(&format!(
74 " \x1b[36m-->\x1b[0m {}:\x1b[34m{}:{}\x1b[0m\n",
75 self.filename, line, col
76 ));
77 } else {
78 output.push_str(&format!(" --> {}:{}:{}\n", self.filename, line, col));
79 }
80
81 let line_num_width = line.to_string().len();
83 output.push_str(&format!("{:width$} |\n", "", width = line_num_width + 1));
84
85 if self.use_colors {
86 output.push_str(&format!(
87 "\x1b[34m{:>width$}\x1b[0m | {}\n",
88 line,
89 source_line,
90 width = line_num_width
91 ));
92 } else {
93 output.push_str(&format!(
94 "{:>width$} | {}\n",
95 line,
96 source_line,
97 width = line_num_width
98 ));
99 }
100
101 let padding = col - 1;
103 let carets = "^".repeat(caret_count);
104 if self.use_colors {
105 output.push_str(&format!(
106 "{:width$} | {:padding$}\x1b[31m{}\x1b[0m\n",
107 "",
108 "",
109 carets,
110 width = line_num_width + 1,
111 padding = padding
112 ));
113 } else {
114 output.push_str(&format!(
115 "{:width$} | {:padding$}{}\n",
116 "",
117 "",
118 carets,
119 width = line_num_width + 1,
120 padding = padding
121 ));
122 }
123 }
124
125 output
126 }
127}