1use super::loc::{Region, RegionProvider};
3use colored::*;
4use std::{cell::RefCell, fmt::Display, io, rc::Rc};
5
6#[repr(i32)]
7#[derive(Clone, Copy, Debug)]
8pub enum ErrorCode {
9 UnknownError,
10 UnrecognizedCharacter,
11 UnexpectedEOF,
12 UnexpectedToken,
13 InvalidTopLevelConstruct,
14 ConstructShouldBeTopLevel,
15 InvalidTokenForStatement,
16 InvalidOperatorSyntax,
17 MalformedType,
18 UnboundName,
19 StaticAnalysisIssue
20}
21
22impl Display for ErrorCode {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 (*self as i32).fmt(f)
25 }
26}
27
28impl Default for ErrorCode {
29 fn default() -> Self {
30 Self::UnknownError
31 }
32}
33
34#[derive(PartialEq, Eq, Debug)]
35pub enum Level {
36 Info,
37 Note,
38 Warning,
39 Error
40}
41
42impl Level {
43 fn color(&self) -> Color {
44 match self {
45 Self::Info => Color::BrightWhite,
46 Self::Note => Color::BrightYellow,
47 Self::Warning => Color::Yellow,
48 Self::Error => Color::BrightRed
49 }
50 }
51
52 fn form_header(&self, code: ErrorCode) -> ColoredString {
53 let string = self.to_string();
54 format!(
55 "{}[{}{:04}]",
56 string,
57 string.chars().next().unwrap().to_ascii_uppercase(),
58 code
59 )
60 .color(self.color())
61 }
62}
63
64impl Display for Level {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 write!(
67 f,
68 "{}",
69 match self {
70 Self::Info => "info",
71 Self::Note => "note",
72 Self::Warning => "warning",
73 Self::Error => "error"
74 }
75 )
76 }
77}
78
79#[derive(PartialEq, Eq, Debug)]
80pub enum Style {
81 Primary,
82 Secondary
83}
84
85#[derive(Debug)]
88pub struct Error {
89 style: Style,
90 level: Level,
91 code: ErrorCode,
92 region: Option<Region>,
93 message: String,
94 explain: Option<String>,
95 fix: Option<String>
96}
97
98impl Display for Error {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 if self.style == Style::Primary {
103 write!(f, "{}: ", self.level.form_header(self.code).bold(),)?;
104 if self.region.is_some() {
105 write!(
106 f,
107 "{}: ",
108 self.region.as_ref().unwrap().start.to_string().underline()
109 )?;
110 }
111 }
112 writeln!(f, "{}", self.message.bold())?;
113
114 if self.region.is_none() {
117 return Ok(());
118 }
119
120 let region = self.region.as_ref().unwrap();
123 let region_extra_lines = (region.end.line - region.start.line) as usize;
124 let show_lines_before = 1;
125 let show_lines_after = 1;
126 let mut already_explained = false;
127 writeln!(f, "{}", " │ ".dimmed())?;
128 let (lines, current_line_pos) = region
129 .start
130 .lines(show_lines_before, region_extra_lines + show_lines_after);
131 let show_start_line =
132 region.start_line() - (show_lines_before as isize);
133 let region_line_sections =
134 region.find_intersection(&lines, show_start_line);
135 for (i, line) in lines.iter().enumerate() {
136 if i > 0 {
137 writeln!(f)?;
138 }
139 write!(
140 f,
141 "{}",
142 format!(
143 "{: >4} │ ",
144 i + (region.start.line as usize) - current_line_pos
145 )
146 .dimmed()
147 )?;
148 if i >= current_line_pos
149 && i <= current_line_pos + region_extra_lines
150 {
151 let line_section = ®ion_line_sections[i - current_line_pos];
152 let split_first = line_section.start;
153 let (part1, rest) = line.split_at(split_first as usize);
154 if !line.is_empty() {
155 let split_second =
156 split_first + (line_section.length() as isize) - 1;
157 let (part2, part3) = if (split_second as usize)
158 == line.len()
159 {
160 (rest, "")
161 } else {
162 rest.split_at((split_second - split_first + 1) as usize)
163 };
164 write!(
165 f,
166 "{}{}{}",
167 part1,
168 part2.color(self.level.color()),
169 if part3.is_empty() {
170 " ".on_color(self.level.color())
171 } else {
172 part3.into()
173 }
174 )?;
175 } else {
176 write!(f, "{}{}", part1, " ".on_color(self.level.color()))?;
177 }
178 if let Some(explain) =
179 &self.explain.as_ref().filter(|_| !already_explained)
180 {
181 already_explained = true;
182 fn create_error_pointer(length: usize) -> String {
183 match length {
184 0 => "".into(),
185 n => format!("└{}", "─".repeat(n - 1))
186 }
187 }
188 writeln!(f)?;
189 write!(
190 f,
191 "{} {}{} {}",
192 " │".dimmed(),
193 " ".repeat(part1.len()),
194 create_error_pointer(line_section.length())
195 .color(self.level.color()),
196 explain.bold().italic()
197 )?;
198 }
199 } else {
200 write!(f, "{}", line)?;
201 }
202 }
203 write!(f, "\n{}", " │ ".dimmed())?;
204 if let Some(fix) = &self.fix {
205 write!(f, "\nSuggestion: {}", fix.bold())?;
206 }
207 Ok(())
208 }
209}
210
211impl Default for Error {
212 fn default() -> Self {
213 Error {
214 style: Style::Primary,
215 level: Level::Error,
216 code: ErrorCode::default(),
217 region: None,
218 message: String::default(),
219 explain: None,
220 fix: None
221 }
222 }
223}
224
225pub struct ErrorBuilder {
227 error: Error
228}
229
230impl ErrorBuilder {
231 pub fn new() -> Self {
233 ErrorBuilder {
234 error: Error::default()
235 }
236 }
237
238 pub fn of_style(mut self, style: Style) -> Self {
240 self.error.style = style;
241 self
242 }
243
244 pub fn at_level(mut self, level: Level) -> Self {
246 self.error.level = level;
247 self
248 }
249
250 pub fn with_code(mut self, code: ErrorCode) -> Self {
252 self.error.code = code;
253 self
254 }
255
256 pub fn at_region<R: RegionProvider>(mut self, region_provider: &R) -> Self {
258 self.error.region = Some(region_provider.region());
259 self
260 }
261
262 pub fn without_loc(mut self) -> Self {
264 self.error.region = None;
265 self
266 }
267
268 pub fn message(mut self, message: String) -> Self {
270 self.error.message = message;
271 self
272 }
273
274 pub fn continues(self) -> Self {
280 assert!(self.error.style == Style::Secondary);
281 self.message(" ...".into())
282 }
283
284 pub fn explain(mut self, explain: String) -> Self {
286 self.error.explain = Some(explain);
287 self
288 }
289
290 pub fn fix(mut self, fix: String) -> Self {
292 self.error.fix = Some(fix);
293 self
294 }
295
296 pub fn build(self) -> Error {
298 self.error
299 }
300}
301
302pub struct ErrorManager {
304 max_count: usize,
305 primary_count: usize,
306 errors: Vec<Error>
307}
308
309impl ErrorManager {
310 pub fn with_max_count(max_count: usize) -> Rc<RefCell<ErrorManager>> {
313 Rc::new(RefCell::new(ErrorManager {
314 max_count,
315 primary_count: 0,
316 errors: vec![]
317 }))
318 }
319
320 pub fn has_errors(&self) -> bool {
322 !self.errors.is_empty()
323 }
324
325 pub fn is_full(&self) -> bool {
327 self.primary_count == self.max_count
328 }
329
330 pub fn record(&mut self, error: Error) -> bool {
332 if self.is_full() {
333 false
334 } else {
335 if error.style == Style::Primary && error.level == Level::Error {
336 self.primary_count += 1
337 }
338 self.errors.push(error);
339 true
340 }
341 }
342
343 pub fn consume_and_write<W: io::Write>(
345 &mut self, output: &mut W
346 ) -> io::Result<()> {
347 for (i, error) in self.errors.iter().enumerate() {
348 if error.style == Style::Primary && i > 0 {
349 output.write_all(&[b'\n'])?;
350 }
351 output.write_all(error.to_string().as_bytes())?;
352 output.write_all(&[b'\n'])?;
353 output.flush()?;
354 }
355 self.errors.clear();
356 self.primary_count = 0;
357 Ok(())
358 }
359}