miden_diagnostics/
handler.rs1use std::io::Write;
2use std::ops::Deref;
3use std::sync::atomic::{AtomicUsize, Ordering};
4use std::sync::Arc;
5
6use crate::term::termcolor::{Color, ColorSpec, WriteColor};
7use crate::*;
8
9pub struct DiagnosticsHandler {
21 emitter: Arc<dyn Emitter>,
22 pub(crate) codemap: Arc<CodeMap>,
23 err_count: AtomicUsize,
24 verbosity: Verbosity,
25 warnings_as_errors: bool,
26 no_warn: bool,
27 silent: bool,
28 pub(crate) display: crate::term::Config,
29}
30
31unsafe impl Send for DiagnosticsHandler {}
34unsafe impl Sync for DiagnosticsHandler {}
35
36impl DiagnosticsHandler {
37 pub fn new(
40 config: DiagnosticsConfig,
41 codemap: Arc<CodeMap>,
42 emitter: Arc<dyn Emitter>,
43 ) -> Self {
44 let no_warn = config.no_warn || config.verbosity > Verbosity::Warning;
45 Self {
46 emitter,
47 codemap,
48 err_count: AtomicUsize::new(0),
49 verbosity: config.verbosity,
50 warnings_as_errors: config.warnings_as_errors,
51 no_warn,
52 silent: config.verbosity == Verbosity::Silent,
53 display: config.display,
54 }
55 }
56
57 pub fn lookup_file_id(&self, filename: impl Into<FileName>) -> Option<SourceId> {
59 let filename = filename.into();
60 self.codemap.get_file_id(&filename)
61 }
62
63 pub fn has_errors(&self) -> bool {
65 self.err_count.load(Ordering::Relaxed) > 0
66 }
67
68 #[track_caller]
70 pub fn abort_if_errors(&self) {
71 if self.has_errors() {
72 FatalError.raise();
73 }
74 }
75
76 pub fn fatal(&self, err: impl ToString) -> FatalError {
79 self.error(err);
80 FatalError
81 }
82
83 pub fn error(&self, error: impl ToString) {
85 let diagnostic = Diagnostic::error().with_message(error.to_string());
86 self.emit(diagnostic);
87 }
88
89 pub fn warn(&self, warning: impl ToString) {
93 if self.warnings_as_errors {
94 return self.error(warning);
95 }
96 let diagnostic = Diagnostic::warning().with_message(warning.to_string());
97 self.emit(diagnostic);
98 }
99
100 pub fn info(&self, message: impl ToString) {
102 if self.verbosity > Verbosity::Info {
103 return;
104 }
105 let info_color = self.display.styles.header(Severity::Help);
106 let mut buffer = self.emitter.buffer();
107 buffer.set_color(info_color).ok();
108 buffer.write_all(b"info").unwrap();
109 buffer.set_color(&self.display.styles.header_message).ok();
110 writeln!(&mut buffer, ": {}", message.to_string()).unwrap();
111 buffer.reset().ok();
112 self.emitter.print(buffer).unwrap();
113 }
114
115 pub fn debug(&self, message: impl ToString) {
117 if self.verbosity > Verbosity::Debug {
118 return;
119 }
120 let mut debug_color = self.display.styles.header_message.clone();
121 debug_color.set_fg(Some(Color::Blue));
122 let mut buffer = self.emitter.buffer();
123 buffer.set_color(&debug_color).ok();
124 buffer.write_all(b"debug").unwrap();
125 buffer.set_color(&self.display.styles.header_message).ok();
126 writeln!(&mut buffer, ": {}", message.to_string()).unwrap();
127 buffer.reset().ok();
128 self.emitter.print(buffer).unwrap();
129 }
130
131 pub fn note(&self, message: impl ToString) {
133 if self.verbosity > Verbosity::Info {
134 return;
135 }
136 self.emit(Diagnostic::note().with_message(message.to_string()));
137 }
138
139 pub fn notice(&self, prefix: &str, message: impl ToString) {
144 if self.verbosity > Verbosity::Info {
145 return;
146 }
147 self.write_prefixed(
148 self.display.styles.header(Severity::Warning),
149 prefix,
150 message,
151 );
152 }
153
154 pub fn success(&self, prefix: &str, message: impl ToString) {
156 if self.silent {
157 return;
158 }
159 self.write_prefixed(self.display.styles.header(Severity::Note), prefix, message);
160 }
161
162 pub fn failed(&self, prefix: &str, message: impl ToString) {
164 self.err_count.fetch_add(1, Ordering::Relaxed);
165 self.write_prefixed(self.display.styles.header(Severity::Error), prefix, message);
166 }
167
168 fn write_prefixed(&self, color: &ColorSpec, prefix: &str, message: impl ToString) {
169 let mut buffer = self.emitter.buffer();
170 buffer.set_color(color).ok();
171 write!(&mut buffer, "{:>12} ", prefix).unwrap();
172 buffer.reset().ok();
173 let message = message.to_string();
174 buffer.write_all(message.as_bytes()).unwrap();
175 self.emitter.print(buffer).unwrap();
176 }
177
178 pub fn diagnostic(&self, severity: Severity) -> InFlightDiagnostic<'_> {
183 InFlightDiagnostic::new(self, severity)
184 }
185
186 #[inline(always)]
188 pub fn emit(&self, diagnostic: impl ToDiagnostic) {
189 if self.silent {
190 return;
191 }
192
193 let mut diagnostic = diagnostic.to_diagnostic();
194 match diagnostic.severity {
195 Severity::Note if self.verbosity > Verbosity::Info => return,
196 Severity::Warning if self.no_warn => return,
197 Severity::Warning if self.warnings_as_errors => {
198 diagnostic.severity = Severity::Error;
199 }
200 _ => (),
201 }
202
203 if diagnostic.severity == Severity::Error {
204 self.err_count.fetch_add(1, Ordering::Relaxed);
205 }
206
207 let mut buffer = self.emitter.buffer();
208 crate::term::emit(
209 &mut buffer,
210 &self.display,
211 self.codemap.deref(),
212 &diagnostic,
213 )
214 .unwrap();
215 self.emitter.print(buffer).unwrap();
216 }
217}