1mod compact;
8mod diagnostic;
9mod formatter;
10#[cfg(feature = "json")]
11mod json;
12mod macros;
13mod smart;
14mod style;
15
16pub use compact::format_compact;
17pub use diagnostic::*;
18pub use formatter::{DiagnosticFormatter, RenderOptions, SourceCache};
19pub use smart::{format_all_smart, print_all_smart};
20
21use crate::style::*;
22use colored::*;
23
24#[derive(Debug)]
27pub struct DiagnosticEngine<C: DiagnosticCode> {
28 diagnostics: Vec<Diagnostic<C>>,
29 bug_count: usize,
30 error_count: usize,
31 warning_count: usize,
32 help_count: usize,
33 note_count: usize,
34}
35
36impl<C: DiagnosticCode> Default for DiagnosticEngine<C> {
37 fn default() -> Self {
38 Self {
39 diagnostics: Vec::new(),
40 bug_count: 0,
41 error_count: 0,
42 warning_count: 0,
43 help_count: 0,
44 note_count: 0,
45 }
46 }
47}
48
49impl<C: DiagnosticCode> DiagnosticEngine<C> {
50 pub fn new() -> Self {
52 Self::default()
53 }
54
55 pub fn clear(&mut self) {
57 self.diagnostics.clear();
58 self.bug_count = 0;
59 self.error_count = 0;
60 self.warning_count = 0;
61 self.help_count = 0;
62 self.note_count = 0;
63 }
64
65 pub fn emit(&mut self, diagnostic: Diagnostic<C>) {
67 match diagnostic.severity {
68 Severity::Bug => self.bug_count += 1,
69 Severity::Error => self.error_count += 1,
70 Severity::Warning => self.warning_count += 1,
71 Severity::Help => self.help_count += 1,
72 Severity::Note => self.note_count += 1,
73 }
74 self.diagnostics.push(diagnostic);
75 }
76
77 pub fn emit_errors(&mut self, errors: Vec<Diagnostic<C>>) {
79 for d in errors {
80 self.emit(d);
81 }
82 }
83
84 pub fn emit_warnings(&mut self, warnings: Vec<Diagnostic<C>>) {
86 for d in warnings {
87 self.emit(d);
88 }
89 }
90
91 pub fn emit_helps(&mut self, helps: Vec<Diagnostic<C>>) {
93 for d in helps {
94 self.emit(d);
95 }
96 }
97
98 pub fn emit_notes(&mut self, notes: Vec<Diagnostic<C>>) {
100 for d in notes {
101 self.emit(d);
102 }
103 }
104
105 pub fn extend(&mut self, other: DiagnosticEngine<C>) {
107 self.diagnostics.extend(other.diagnostics);
108 self.bug_count += other.bug_count;
109 self.error_count += other.error_count;
110 self.warning_count += other.warning_count;
111 self.help_count += other.help_count;
112 self.note_count += other.note_count;
113 }
114
115 pub fn print_all(&self, source_code: &str) {
119 let cache = SourceCache::new(source_code);
120 for d in &self.diagnostics {
121 let f = DiagnosticFormatter::with_cache(d, &cache);
122 print!("{}", f.format());
123 }
124 let summary = self.format_summary();
125 if !summary.is_empty() {
126 println!("\n{}", summary);
127 }
128 }
129
130 pub fn format_all(&self, source_code: &str) -> String {
132 self.format_all_with(source_code, RenderOptions::default())
133 }
134
135 pub fn format_all_plain(&self, source_code: &str) -> String {
138 let opts = RenderOptions { color: false, ..Default::default() };
139 self.format_all_with(source_code, opts)
140 }
141
142 pub fn format_all_with(&self, source_code: &str, options: RenderOptions) -> String {
144 let cache = SourceCache::new(source_code);
145 let mut out = String::new();
146 for d in &self.diagnostics {
147 let f = DiagnosticFormatter::with_cache(d, &cache).with_options(options);
148 out.push_str(&f.format());
149 }
150 if options.color {
151 out.push_str(&self.format_summary());
152 } else {
153 out.push_str(&self.format_summary_plain());
154 }
155 out
156 }
157
158 pub fn format_all_compact(&self) -> String {
164 self.format_all_compact_with(true)
165 }
166
167 pub fn format_all_compact_plain(&self) -> String {
169 self.format_all_compact_with(false)
170 }
171
172 fn format_all_compact_with(&self, color: bool) -> String {
173 let mut out = String::new();
174 for d in &self.diagnostics {
175 out.push_str(&compact::format_compact(d, color));
176 }
177 if color {
178 out.push_str(&self.format_summary());
179 } else {
180 out.push_str(&self.format_summary_plain());
181 }
182 out
183 }
184
185 pub fn print_all_compact(&self) {
188 print!("{}", self.format_all_compact());
189 let summary = self.format_summary();
190 if !summary.is_empty() {
191 println!("\n{}", summary);
192 }
193 }
194
195 pub fn summary(&self, color: bool) -> String {
199 self.format_summary_with(color)
200 }
201
202 fn format_summary(&self) -> String {
203 self.format_summary_with(true)
204 }
205
206 fn format_summary_plain(&self) -> String {
207 self.format_summary_with(false)
208 }
209
210 fn format_summary_with(&self, color: bool) -> String {
211 if self.error_count + self.warning_count + self.bug_count == 0 {
212 return String::new();
213 }
214 let total_errors = self.error_count + self.bug_count;
215 if total_errors > 0 {
216 let warn_part = if self.warning_count > 0 {
217 format!(
218 "; {} {} emitted",
219 paint(&self.warning_count.to_string(), color, |s| s.yellow().bold()),
220 plural("warning", self.warning_count),
221 )
222 } else {
223 String::new()
224 };
225 format!(
226 "{}: could not compile due to {} previous {}{}",
227 paint("error", color, |s| s.red().bold()),
228 paint(&total_errors.to_string(), color, |s| s.red().bold()),
229 plural("error", total_errors),
230 warn_part,
231 )
232 } else {
233 format!(
234 "{}: {} {} emitted",
235 paint("warning", color, |s| s.yellow().bold()),
236 paint(&self.warning_count.to_string(), color, |s| s.yellow().bold()),
237 plural("warning", self.warning_count),
238 )
239 }
240 }
241
242 pub fn get_diagnostics(&self) -> &[Diagnostic<C>] {
246 &self.diagnostics
247 }
248
249 pub fn iter(&self) -> std::slice::Iter<'_, Diagnostic<C>> {
251 self.diagnostics.iter()
252 }
253
254 pub fn get_errors(&self) -> Vec<&Diagnostic<C>> {
256 self.diagnostics.iter().filter(|d| d.severity == Severity::Error).collect()
257 }
258
259 pub fn get_warnings(&self) -> Vec<&Diagnostic<C>> {
261 self.diagnostics.iter().filter(|d| d.severity == Severity::Warning).collect()
262 }
263
264 pub fn get_notes(&self) -> Vec<&Diagnostic<C>> {
266 self.diagnostics.iter().filter(|d| d.severity == Severity::Note).collect()
267 }
268
269 pub fn get_helps(&self) -> Vec<&Diagnostic<C>> {
271 self.diagnostics.iter().filter(|d| d.severity == Severity::Help).collect()
272 }
273
274 pub fn get_bugs(&self) -> Vec<&Diagnostic<C>> {
276 self.diagnostics.iter().filter(|d| d.severity == Severity::Bug).collect()
277 }
278
279 pub fn is_empty(&self) -> bool {
281 self.diagnostics.is_empty()
282 }
283
284 pub fn len(&self) -> usize {
286 self.diagnostics.len()
287 }
288
289 pub fn has_errors(&self) -> bool {
291 self.error_count > 0
292 }
293
294 pub fn has_warnings(&self) -> bool {
296 self.warning_count > 0
297 }
298
299 pub fn has_helps(&self) -> bool {
301 self.help_count > 0
302 }
303
304 pub fn has_notes(&self) -> bool {
306 self.note_count > 0
307 }
308
309 pub fn has_bugs(&self) -> bool {
311 self.bug_count > 0
312 }
313
314 pub fn bug_count(&self) -> usize {
316 self.bug_count
317 }
318
319 pub fn error_count(&self) -> usize {
321 self.error_count
322 }
323
324 pub fn warning_count(&self) -> usize {
326 self.warning_count
327 }
328
329 pub fn help_count(&self) -> usize {
331 self.help_count
332 }
333
334 pub fn note_count(&self) -> usize {
336 self.note_count
337 }
338}
339
340#[cfg(feature = "json")]
341impl<C: DiagnosticCode + serde::Serialize> DiagnosticEngine<C> {
342 pub fn format_all_json(&self) -> String {
345 crate::json::format_all_json(&self.diagnostics)
346 }
347}