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}