1use alloc::{borrow::Cow, boxed::Box, sync::Arc, vec::Vec};
2use core::{fmt, ops::Range};
3
4pub use miette::{
5 self, Diagnostic, IntoDiagnostic, LabeledSpan, NamedSource, Report, Result, Severity,
6 SourceCode, WrapErr,
7};
8pub use tracing;
9pub use vm_core::debuginfo::*;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Label {
20 span: miette::SourceSpan,
21 label: Option<Cow<'static, str>>,
22}
23
24impl Label {
25 pub fn at<R>(range: R) -> Self
28 where
29 Range<usize>: From<R>,
30 {
31 let range = Range::<usize>::from(range);
32 Self { span: range.into(), label: None }
33 }
34
35 pub fn point<L>(at: usize, label: L) -> Self
37 where
38 Cow<'static, str>: From<L>,
39 {
40 Self {
41 span: miette::SourceSpan::from(at),
42 label: Some(Cow::from(label)),
43 }
44 }
45
46 pub fn new<R, L>(range: R, label: L) -> Self
48 where
49 Range<usize>: From<R>,
50 Cow<'static, str>: From<L>,
51 {
52 let range = Range::<usize>::from(range);
53 Self {
54 span: range.into(),
55 label: Some(Cow::from(label)),
56 }
57 }
58
59 pub fn label(&self) -> Option<&str> {
61 self.label.as_deref()
62 }
63}
64
65impl From<Label> for miette::SourceSpan {
66 #[inline(always)]
67 fn from(label: Label) -> Self {
68 label.span
69 }
70}
71
72impl From<Label> for LabeledSpan {
73 #[inline]
74 fn from(label: Label) -> LabeledSpan {
75 if let Some(message) = label.label {
76 LabeledSpan::at(label.span, message)
77 } else {
78 LabeledSpan::underline(label.span)
79 }
80 }
81}
82
83#[derive(Debug)]
93pub struct RelatedLabel {
94 pub severity: Severity,
96 pub message: Cow<'static, str>,
98 pub labels: Vec<Label>,
100 pub file: Option<Arc<SourceFile>>,
102}
103
104impl fmt::Display for RelatedLabel {
105 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106 f.write_str(self.message.as_ref())
107 }
108}
109
110#[cfg(feature = "std")]
111impl std::error::Error for RelatedLabel {
112 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
113 None
114 }
115}
116
117#[cfg(not(feature = "std"))]
118impl miette::StdError for RelatedLabel {
119 fn source(&self) -> Option<&(dyn miette::StdError + 'static)> {
120 None
121 }
122}
123
124impl RelatedLabel {
125 pub fn new<S>(severity: Severity, message: S) -> Self
126 where
127 Cow<'static, str>: From<S>,
128 {
129 Self {
130 severity,
131 message: Cow::from(message),
132 labels: vec![],
133 file: None,
134 }
135 }
136
137 pub fn error<S>(message: S) -> Self
138 where
139 Cow<'static, str>: From<S>,
140 {
141 Self::new(Severity::Error, message)
142 }
143
144 #[allow(unused)]
145 pub fn warning<S>(message: S) -> Self
146 where
147 Cow<'static, str>: From<S>,
148 {
149 Self::new(Severity::Warning, message)
150 }
151
152 #[allow(unused)]
153 pub fn advice<S>(message: S) -> Self
154 where
155 Cow<'static, str>: From<S>,
156 {
157 Self::new(Severity::Advice, message)
158 }
159
160 pub fn with_source_file(mut self, file: Option<Arc<SourceFile>>) -> Self {
161 self.file = file;
162 self
163 }
164
165 pub fn with_labeled_span<S>(self, span: SourceSpan, message: S) -> Self
166 where
167 Cow<'static, str>: From<S>,
168 {
169 let range = span.into_range();
170 self.with_label(Label::new((range.start as usize)..(range.end as usize), message))
171 }
172
173 pub fn with_label(mut self, label: Label) -> Self {
174 self.labels.push(label);
175 self
176 }
177
178 #[allow(unused)]
179 pub fn with_labels<I>(mut self, labels: I) -> Self
180 where
181 I: IntoIterator<Item = Label>,
182 {
183 self.labels.extend(labels);
184 self
185 }
186}
187
188impl Diagnostic for RelatedLabel {
189 fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
190 None
191 }
192 fn severity(&self) -> Option<Severity> {
193 Some(self.severity)
194 }
195 fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
196 None
197 }
198 fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
199 None
200 }
201 fn source_code(&self) -> Option<&dyn SourceCode> {
202 self.file.as_ref().map(|f| f as &dyn SourceCode)
203 }
204 fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
205 if self.labels.is_empty() {
206 None
207 } else {
208 Some(Box::new(self.labels.iter().cloned().map(|l| l.into())))
209 }
210 }
211 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
212 None
213 }
214 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
215 None
216 }
217}
218
219#[derive(Debug)]
226pub struct RelatedError(Report);
227
228impl RelatedError {
229 pub fn into_report(self) -> Report {
230 self.0
231 }
232
233 #[inline(always)]
234 pub fn as_diagnostic(&self) -> &dyn Diagnostic {
235 self.0.as_ref()
236 }
237}
238
239impl Diagnostic for RelatedError {
240 fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
241 self.as_diagnostic().code()
242 }
243 fn severity(&self) -> Option<Severity> {
244 self.as_diagnostic().severity()
245 }
246 fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
247 self.as_diagnostic().help()
248 }
249 fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
250 self.as_diagnostic().url()
251 }
252 fn source_code(&self) -> Option<&dyn SourceCode> {
253 self.as_diagnostic().source_code()
254 }
255 fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
256 self.as_diagnostic().labels()
257 }
258 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
259 self.as_diagnostic().related()
260 }
261 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
262 self.as_diagnostic().diagnostic_source()
263 }
264}
265
266impl fmt::Display for RelatedError {
267 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
268 fmt::Display::fmt(&self.0, f)
269 }
270}
271
272#[cfg(feature = "std")]
273impl std::error::Error for RelatedError {
274 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
275 AsRef::<dyn std::error::Error>::as_ref(&self.0).source()
276 }
277}
278
279#[cfg(not(feature = "std"))]
280impl miette::StdError for RelatedError {
281 fn source(&self) -> Option<&(dyn miette::StdError + 'static)> {
282 AsRef::<dyn miette::StdError>::as_ref(&self.0).source()
283 }
284}
285
286impl From<Report> for RelatedError {
287 fn from(report: Report) -> Self {
288 Self(report)
289 }
290}
291
292impl RelatedError {
293 pub const fn new(report: Report) -> Self {
294 Self(report)
295 }
296
297 pub fn wrap<E>(error: E) -> Self
298 where
299 E: Diagnostic + Send + Sync + 'static,
300 {
301 Self(Report::new_boxed(Box::new(error)))
302 }
303}
304
305pub mod reporting {
310 use core::fmt;
311
312 pub use miette::{
313 DebugReportHandler, JSONReportHandler, NarratableReportHandler, ReportHandler, set_hook,
314 };
315 #[cfg(feature = "std")]
316 pub use miette::{GraphicalReportHandler, GraphicalTheme, set_panic_hook};
317
318 pub type ReportHandlerOpts = miette::MietteHandlerOpts;
319
320 #[cfg(feature = "std")]
321 pub type DefaultReportHandler = miette::GraphicalReportHandler;
322
323 #[cfg(not(feature = "std"))]
324 pub type DefaultReportHandler = miette::DebugReportHandler;
325
326 pub struct PrintDiagnostic<D, R = DefaultReportHandler> {
328 handler: R,
329 diag: D,
330 }
331
332 impl<D: AsRef<dyn super::Diagnostic>> PrintDiagnostic<D> {
333 pub fn new(diag: D) -> Self {
334 Self { handler: Default::default(), diag }
335 }
336 #[cfg(feature = "std")]
337 pub fn new_without_color(diag: D) -> Self {
338 Self {
339 handler: DefaultReportHandler::new_themed(GraphicalTheme::none()),
340 diag,
341 }
342 }
343 #[cfg(not(feature = "std"))]
344 pub fn new_without_color(diag: D) -> Self {
345 Self::new(diag)
346 }
347 }
348
349 impl<D: AsRef<dyn super::Diagnostic>> PrintDiagnostic<D, NarratableReportHandler> {
350 pub fn narrated(diag: D) -> Self {
351 Self {
352 handler: NarratableReportHandler::default(),
353 diag,
354 }
355 }
356 }
357
358 impl<D: AsRef<dyn super::Diagnostic>> PrintDiagnostic<D, JSONReportHandler> {
359 pub fn json(diag: D) -> Self {
360 Self { handler: JSONReportHandler, diag }
361 }
362 }
363
364 impl<D: AsRef<dyn super::Diagnostic>> fmt::Display for PrintDiagnostic<D> {
365 fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
366 self.handler.render_report(f, self.diag.as_ref())
367 }
368 }
369
370 impl<D: AsRef<dyn super::Diagnostic>> fmt::Display for PrintDiagnostic<D, NarratableReportHandler> {
371 fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
372 self.handler.render_report(f, self.diag.as_ref())
373 }
374 }
375
376 impl<D: AsRef<dyn super::Diagnostic>> fmt::Display for PrintDiagnostic<D, JSONReportHandler> {
377 fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
378 self.handler.render_report(f, self.diag.as_ref())
379 }
380 }
381}