flycatcher_diagnostic/lib.rs
1//! A crate for emitting diagnostic messages emitted by Flycatcher to the terminal.
2
3use codespan_reporting::diagnostic::{Diagnostic, Severity};
4use codespan_reporting::files::SimpleFile;
5use codespan_reporting::term::{
6 Chars,
7 Config,
8 emit,
9 Styles,
10 termcolor::{
11 Color,
12 ColorChoice,
13 ColorSpec,
14 StandardStream
15 }
16};
17
18pub use codespan_reporting;
19
20/// A tool for emitting diagnostic messages to the terminal.
21pub struct DiagnosticEmitter<'a> {
22
23 /// The configuration used for displaying diagnostic messages. The default is similar to
24 /// the way RustC displays diagnostic messages.
25 pub config: Config,
26
27 /// The name of the file that the diagnostic messages should say. If you don't know what to
28 /// use here, just use `@anonymous`.
29 pub filename: &'a str,
30
31 /// The source of the file, used by diagnostic messages to provide a snippet of the
32 /// offending code.
33 pub source: &'a str,
34
35}
36
37impl<'a> DiagnosticEmitter<'a> {
38
39 /// Allocates a new DiagnosticEmitter that will emit diagnostic messages with the file path
40 /// provided.
41 ///
42 /// # Arguments
43 /// - `filename`: This is the filename that will be displayed in the diagnostic messages
44 /// when they are emitted to the console.
45 /// - `source`: The contents of the file that the diagnostics are for.
46 pub fn new(filename: &'a str, source: &'a str) -> Self {
47 // Use only ASCII characters, similar to Rustc's diagnostic messages.
48 let mut config = Config::default();
49 config.chars = Chars::ascii();
50
51 let mut styles = Styles::default();
52
53 {
54 // Set the primary label bug to be Red intense, similar to Rustc's diagnostics.
55 let mut style = ColorSpec::new();
56 style.set_bold(true);
57 style.set_fg(Some(Color::Red));
58 style.set_intense(true);
59 styles.primary_label_bug = style;
60 }
61
62 {
63 // Set the primary label error to be Red intense, similar to Rustc's diagnostics.
64 let mut style = ColorSpec::new();
65 style.set_bold(true);
66 style.set_fg(Some(Color::Red));
67 style.set_intense(true);
68 styles.primary_label_error = style;
69 }
70
71 {
72 // Set the source border to be Cyan intense, similar to Rustc's diagnostics.
73 let mut style = ColorSpec::new();
74 style.set_bold(true);
75 style.set_fg(Some(Color::Cyan));
76 style.set_intense(true);
77 styles.source_border = style;
78 }
79
80 {
81 // Set the note bullet to be Cyan intense, similar to Rustc's diagnostics.
82 let mut style = ColorSpec::new();
83 style.set_bold(true);
84 style.set_fg(Some(Color::Cyan));
85 style.set_intense(true);
86 styles.note_bullet = style;
87 }
88
89 {
90 // Set the line numbers to be Cyan intense, similar to Rustc's diagnostics.
91 let mut style = ColorSpec::new();
92 style.set_bold(true);
93 style.set_fg(Some(Color::Cyan));
94 style.set_intense(true);
95 styles.line_number = style;
96 }
97
98 {
99 // Set the warning color to be Yellow intense, similar to Rustc's diagnostics.
100 let mut style = ColorSpec::new();
101 style.set_bold(true);
102 style.set_fg(Some(Color::Yellow));
103 style.set_intense(true);
104 styles.primary_label_warning = style;
105 }
106
107 {
108 // Set the note header color to be Cyan intense, similar to Rustc's diagnostics.
109 let mut style = ColorSpec::new();
110 style.set_bold(true);
111 style.set_fg(Some(Color::Cyan));
112 style.set_intense(true);
113 styles.header_note = style;
114 }
115
116 {
117 // Set the note label color to be Cyan intense, similar to Rustc's diagnostics.
118 let mut style = ColorSpec::new();
119 style.set_bold(true);
120 style.set_fg(Some(Color::Cyan));
121 style.set_intense(true);
122 styles.primary_label_note = style;
123 }
124
125 {
126 // Set the secondary label color to be Cyan intense, similar to Rustc's diagnostics.
127 let mut style = ColorSpec::new();
128 style.set_bold(true);
129 style.set_fg(Some(Color::Cyan));
130 style.set_intense(true);
131 styles.secondary_label = style;
132 }
133
134 // Use the styles defined above.
135 config.styles = styles;
136
137 Self {
138 config,
139 filename,
140 source
141 }
142 }
143
144 /// Emits a single diagnostic message to the console.
145 ///
146 /// # Arguments
147 /// - `diagnostic`: The diagnostic message to display.
148 pub fn emit_diagnostic(&self, diagnostic: Diagnostic<()>) {
149 // Create the `codespan_reporting` file, which contains the information about the source
150 // used in the diagnostic. This is used to get the filename and source for the
151 // diagnostic previews.
152 let simple_file = SimpleFile::new(self.filename.to_string(), self.source);
153
154 // Use `stderr` for bugs and errors and `stdout` for warnings, notes and help
155 // diagnostics.
156 let mut stream = match diagnostic.severity {
157 Severity::Bug |
158 Severity::Error => StandardStream::stderr(ColorChoice::Auto),
159 _ => StandardStream::stdout(ColorChoice::Auto)
160 };
161
162 // Check if an error occurs while simultaniously emitting the diagnostic to the console.
163 if let Err(e) = emit(&mut stream, &self.config, &simple_file, &diagnostic) {
164 panic!("{}", e);
165 }
166 }
167
168 /// Emits a list of diagnostics to the terminal.
169 ///
170 /// # Arguments
171 /// - `diagnostics`: The vector of diagnostics to log to the console.
172 pub fn emit(&self, diagnostics: Vec<Diagnostic<()>>) {
173 for diag in diagnostics {
174 self.emit_diagnostic(diag);
175 }
176 }
177
178}