1mod diagnostic;
2mod formatter;
3mod utils;
4
5pub use diagnostic::*;
6pub use formatter::*;
7
8use crate::utils::*;
9use colored::*;
10
11#[derive(Debug)]
12pub struct DiagnosticEngine<C: DiagnosticCode> {
13 diagnostics: Vec<Diagnostic<C>>,
14 error_count: usize,
15 warning_count: usize,
16 help_count: usize,
17 note_count: usize,
18}
19
20impl<C: DiagnosticCode> Default for DiagnosticEngine<C> {
21 fn default() -> Self {
22 Self { diagnostics: Vec::new(), error_count: 0, warning_count: 0, help_count: 0, note_count: 0 }
23 }
24}
25
26impl<C: DiagnosticCode> DiagnosticEngine<C> {
27 pub fn new() -> Self {
28 Self::default()
29 }
30
31 pub fn clear(&mut self) {
32 self.diagnostics.clear();
33 self.error_count = 0;
34 self.warning_count = 0;
35 self.help_count = 0;
36 self.note_count = 0;
37 }
38
39 pub fn emit(&mut self, diagnostic: Diagnostic<C>) {
40 match diagnostic.severity {
41 Severity::Error => self.error_count += 1,
42 Severity::Warning => self.warning_count += 1,
43 Severity::Help => self.help_count += 1,
44 Severity::Note => self.note_count += 1,
45 }
46 self.diagnostics.push(diagnostic);
47 }
48
49 pub fn emit_errors(&mut self, errors: Vec<Diagnostic<C>>) {
50 for error in errors {
51 self.emit(error);
52 }
53 }
54
55 pub fn emit_warnings(&mut self, warnings: Vec<Diagnostic<C>>) {
56 for warning in warnings {
57 self.emit(warning);
58 }
59 }
60
61 pub fn emit_helps(&mut self, helps: Vec<Diagnostic<C>>) {
62 for help in helps {
63 self.emit(help);
64 }
65 }
66
67 pub fn emit_notes(&mut self, notes: Vec<Diagnostic<C>>) {
68 for note in notes {
69 self.emit(note);
70 }
71 }
72
73 pub fn extend(&mut self, other: DiagnosticEngine<C>) {
74 self.diagnostics.extend(other.diagnostics);
75 self.error_count += other.error_count;
76 self.warning_count += other.warning_count;
77 self.help_count += other.help_count;
78 self.note_count += other.note_count;
79 }
80
81 pub fn print_all(&self, source_code: &str) {
82 for diagnostic in &self.diagnostics {
83 let formatter = DiagnosticFormatter::new(diagnostic, source_code);
84 print!("{}", formatter.format());
85 }
86 self.print_summary();
87 }
88
89 pub fn format_all(&self, source_code: &str) -> String {
90 let mut output = String::new();
91 for diagnostic in &self.diagnostics {
92 let formatter = DiagnosticFormatter::new(diagnostic, source_code);
93 output.push_str(&formatter.format());
94 }
95 output.push_str(&self.format_summary());
96 output
97 }
98
99 pub fn format_all_plain(&self, source_code: &str) -> String {
100 let mut output = String::new();
101 for diagnostic in &self.diagnostics {
102 let formatter = DiagnosticFormatter::new(diagnostic, source_code);
103 output.push_str(&formatter.format_plain());
104 output.push('\n');
105 }
106 output.push_str(&self.format_summary_plain());
107 output
108 }
109
110 fn print_summary(&self) {
111 let summary = self.format_summary();
112 if !summary.is_empty() {
113 println!("\n{}", summary);
114 }
115 }
116
117 fn format_summary(&self) -> String {
118 if self.error_count == 0 && self.warning_count == 0 {
119 return String::new();
120 }
121
122 if self.has_errors() {
123 let warn_part = if self.warning_count > 0 {
124 format!(
125 "; {} {} emitted",
126 self.warning_count.to_string().yellow().bold(),
127 pluralize("warning", self.warning_count)
128 )
129 } else {
130 String::new()
131 };
132 format!(
133 "{}: could not compile due to {} previous {}{}",
134 "error".red().bold(),
135 self.error_count.to_string().red().bold(),
136 pluralize("error", self.error_count),
137 warn_part
138 )
139 } else {
140 format!(
141 "{}: {} {} emitted",
142 "warning".yellow().bold(),
143 self.warning_count.to_string().yellow().bold(),
144 pluralize("warning", self.warning_count)
145 )
146 }
147 }
148
149 fn format_summary_plain(&self) -> String {
150 if self.error_count == 0 && self.warning_count == 0 {
151 return String::new();
152 }
153
154 if self.has_errors() {
155 let warn_part = if self.warning_count > 0 {
156 format!("; {} {} emitted", self.warning_count, pluralize("warning", self.warning_count))
157 } else {
158 String::new()
159 };
160 format!(
161 "error: could not compile due to {} previous {}{}",
162 self.error_count,
163 pluralize("error", self.error_count),
164 warn_part
165 )
166 } else {
167 format!(
168 "warning: {} {} emitted",
169 self.warning_count,
170 pluralize("warning", self.warning_count)
171 )
172 }
173 }
174
175 pub fn get_diagnostics(&self) -> &[Diagnostic<C>] {
178 &self.diagnostics
179 }
180
181 pub fn get_errors(&self) -> Vec<&Diagnostic<C>> {
182 self.diagnostics.iter().filter(|d| d.severity == Severity::Error).collect()
183 }
184
185 pub fn get_warnings(&self) -> Vec<&Diagnostic<C>> {
186 self.diagnostics.iter().filter(|d| d.severity == Severity::Warning).collect()
187 }
188
189 pub fn get_notes(&self) -> Vec<&Diagnostic<C>> {
190 self.diagnostics.iter().filter(|d| d.severity == Severity::Note).collect()
191 }
192
193 pub fn get_helps(&self) -> Vec<&Diagnostic<C>> {
194 self.diagnostics.iter().filter(|d| d.severity == Severity::Help).collect()
195 }
196
197 pub fn is_empty(&self) -> bool {
198 self.diagnostics.is_empty()
199 }
200
201 pub fn len(&self) -> usize {
202 self.diagnostics.len()
203 }
204
205 pub fn has_errors(&self) -> bool {
206 self.error_count > 0
207 }
208
209 pub fn has_warnings(&self) -> bool {
210 self.warning_count > 0
211 }
212
213 pub fn has_helps(&self) -> bool {
214 self.help_count > 0
215 }
216
217 pub fn has_notes(&self) -> bool {
218 self.note_count > 0
219 }
220
221 pub fn error_count(&self) -> usize {
222 self.error_count
223 }
224
225 pub fn warning_count(&self) -> usize {
226 self.warning_count
227 }
228
229 pub fn help_count(&self) -> usize {
230 self.help_count
231 }
232
233 pub fn note_count(&self) -> usize {
234 self.note_count
235 }
236}