Skip to main content

duck_diagnostic/
lib.rs

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  // getters
176
177  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}