1use ariadne::{Color, Config, Label, Report, ReportKind};
9
10use crate::span::Span;
11
12#[derive(Debug, Clone)]
14pub struct CompileError {
15 pub category: &'static str,
16 pub span: Span,
17 pub message: String,
18 pub labels: Vec<(Span, String)>,
19 pub notes: Vec<String>,
20 pub suggestions: Vec<Suggestion>,
24}
25
26#[derive(Debug, Clone)]
32pub struct Suggestion {
33 pub message: String,
35 pub edits: Vec<(Span, String)>,
36 pub applicability: Applicability,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum Applicability {
43 MachineApplicable,
45 HasPlaceholders,
48}
49
50impl CompileError {
51 pub fn new(category: &'static str, span: Span, message: impl Into<String>) -> Self {
52 Self {
53 category,
54 span,
55 message: message.into(),
56 labels: Vec::new(),
57 notes: Vec::new(),
58 suggestions: Vec::new(),
59 }
60 }
61
62 pub fn with_label(mut self, span: Span, label: impl Into<String>) -> Self {
63 self.labels.push((span, label.into()));
64 self
65 }
66
67 pub fn with_note(mut self, note: impl Into<String>) -> Self {
68 self.notes.push(note.into());
69 self
70 }
71
72 pub fn with_suggestion(
75 mut self,
76 message: impl Into<String>,
77 edits: Vec<(Span, String)>,
78 applicability: Applicability,
79 ) -> Self {
80 self.suggestions.push(Suggestion {
81 message: message.into(),
82 edits,
83 applicability,
84 });
85 self
86 }
87
88 pub fn report<'a>(
91 &'a self,
92 filename: &'a str,
93 ) -> Report<'a, (&'a str, std::ops::Range<usize>)> {
94 self.report_with_config(filename, Config::default())
95 }
96
97 pub fn report_plain<'a>(
100 &'a self,
101 filename: &'a str,
102 ) -> Report<'a, (&'a str, std::ops::Range<usize>)> {
103 self.report_with_config(filename, Config::default().with_color(false))
104 }
105
106 fn report_with_config<'a>(
107 &'a self,
108 filename: &'a str,
109 config: Config,
110 ) -> Report<'a, (&'a str, std::ops::Range<usize>)> {
111 let primary_span = (filename, self.span.range());
112 let mut builder = Report::build(ReportKind::Error, primary_span.clone())
113 .with_config(config)
114 .with_code(self.category)
115 .with_message(&self.message)
116 .with_label(
117 Label::new(primary_span)
118 .with_message(&self.message)
119 .with_color(Color::Red),
120 );
121
122 for (span, label) in &self.labels {
123 builder = builder.with_label(
124 Label::new((filename, span.range()))
125 .with_message(label)
126 .with_color(Color::Yellow),
127 );
128 }
129
130 for note in &self.notes {
131 builder = builder.with_note(note);
132 }
133
134 builder.finish()
135 }
136}