1use crate::source_tracking::SourceMap;
9use crate::source_tracking::filename::FileName;
10use crate::source_tracking::immutable_string::ImmutableString;
11use crate::source_tracking::{fragment::Fragment, source::SourceId};
12use codespan_reporting::diagnostic::Diagnostic as CRDiagnostic;
13use codespan_reporting::diagnostic::Label;
14use codespan_reporting::files::{Error as CRError, Files};
15use codespan_reporting::term::Config;
16use std::io::Write;
17use std::sync::Mutex;
18use termcolor::{ColorChoice, StandardStream, WriteColor};
19
20#[cfg(doc)]
21use crate::source_tracking::source::Source;
22
23pub use codespan_reporting::diagnostic::Severity;
25
26pub use codespan_reporting::diagnostic::LabelStyle;
28
29static STDOUT_COLOR_CHOICE: Mutex<ColorChoice> = Mutex::new(ColorChoice::Auto);
31
32pub fn set_stdout_color(c: ColorChoice) {
34 *STDOUT_COLOR_CHOICE.lock().unwrap() = c;
35}
36
37pub fn get_stdout_color() -> ColorChoice {
39 *STDOUT_COLOR_CHOICE.lock().unwrap()
40}
41
42#[derive(Debug)]
45pub struct Diagnostic(pub CRDiagnostic<SourceId>);
46
47impl Diagnostic {
48 pub fn new(severity: Severity) -> Self {
50 Diagnostic(CRDiagnostic::new(severity))
51 }
52
53 #[inline]
55 pub fn bug() -> Self {
56 Self::new(Severity::Bug)
57 }
58
59 #[inline]
61 pub fn error() -> Self {
62 Self::new(Severity::Error)
63 }
64
65 #[inline]
67 pub fn warning() -> Self {
68 Self::new(Severity::Warning)
69 }
70
71 #[inline]
73 pub fn note() -> Self {
74 Self::new(Severity::Note)
75 }
76
77 #[inline]
79 pub fn help() -> Self {
80 Self::new(Severity::Help)
81 }
82
83 pub fn with_code(self, code: impl Into<String>) -> Self {
85 Diagnostic(self.0.with_code(code))
86 }
87
88 pub fn with_message(self, message: impl Into<String>) -> Self {
90 Diagnostic(self.0.with_message(message))
91 }
92
93 pub fn with_notes<I: Into<String>>(mut self, notes: impl IntoIterator<Item = I>) -> Self {
95 self.0.notes.extend(notes.into_iter().map(Into::into));
96 self
97 }
98
99 pub fn with_highlights(mut self, highlights: impl IntoIterator<Item = Highlight>) -> Self {
101 self.0.labels.extend(highlights.into_iter().map(Into::into));
102 self
103 }
104
105 pub fn write(
108 &self,
109 map: &SourceMap,
110 writer: &mut dyn WriteColor,
111 config: &Config,
112 ) -> Result<(), CRError> {
113 codespan_reporting::term::emit(writer, config, map, &self.0)
114 }
115
116 pub fn print(&self, map: &SourceMap) -> Result<(), codespan_reporting::files::Error> {
120 let stream = StandardStream::stdout(get_stdout_color());
121 let mut lock = stream.lock();
122 self.write(map, &mut lock, &Config::default())?;
123 lock.flush()?;
124 Ok(())
125 }
126}
127
128#[derive(Clone, Debug)]
130pub struct Highlight {
131 pub style: LabelStyle,
134 pub fragment: Fragment,
136 pub message: String,
139}
140
141impl Highlight {
142 pub fn primary(fragment: Fragment, message: impl Into<String>) -> Self {
144 Highlight {
145 style: LabelStyle::Primary,
146 fragment,
147 message: message.into(),
148 }
149 }
150
151 pub fn secondary(fragment: Fragment, message: impl Into<String>) -> Self {
153 Highlight {
154 style: LabelStyle::Secondary,
155 fragment,
156 message: message.into(),
157 }
158 }
159
160 pub fn with_message(mut self, message: impl Into<String>) -> Self {
162 self.message = message.into();
163 self
164 }
165}
166
167impl From<Highlight> for Label<SourceId> {
168 fn from(value: Highlight) -> Self {
169 Label {
170 style: value.style,
171 file_id: value.fragment.source.id,
172 range: value.fragment.range,
173 message: value.message,
174 }
175 }
176}
177
178impl<'f> Files<'f> for SourceMap {
179 type FileId = SourceId;
180
181 type Name = FileName;
182
183 type Source = ImmutableString;
184
185 fn name(&'f self, id: Self::FileId) -> Result<Self::Name, codespan_reporting::files::Error> {
186 self.get(id)
187 .map(|source| source.name().clone())
188 .ok_or(CRError::FileMissing)
189 }
190
191 fn source(
192 &'f self,
193 id: Self::FileId,
194 ) -> Result<Self::Source, codespan_reporting::files::Error> {
195 self.get(id)
196 .map(|source| source.source().clone())
197 .ok_or(CRError::FileMissing)
198 }
199
200 fn line_index(
201 &'f self,
202 id: Self::FileId,
203 byte_index: usize,
204 ) -> Result<usize, codespan_reporting::files::Error> {
205 Ok(self
206 .get(id)
207 .ok_or(CRError::FileMissing)?
208 .line_index(byte_index))
209 }
210
211 fn line_range(
212 &'f self,
213 id: Self::FileId,
214 line_index: usize,
215 ) -> Result<std::ops::Range<usize>, codespan_reporting::files::Error> {
216 Ok(self
217 .get(id)
218 .ok_or(CRError::FileMissing)?
219 .get_line(line_index)
220 .range)
221 }
222}