1pub use miette::{
2 bail, diagnostic, ByteOffset, Diagnostic, IntoDiagnostic, LabeledSpan, Report, Severity,
3 SourceCode, SourceOffset, SourceSpan, WrapErr,
4};
5
6pub mod reporting {
7 pub use miette::{
8 set_hook, DebugReportHandler, JSONReportHandler, NarratableReportHandler, ReportHandler,
9 };
10
11 #[cfg(feature = "fancy-diagnostics")]
12 pub use miette::{GraphicalReportHandler, GraphicalTheme};
13 #[cfg(feature = "fancy-diagnostics")]
14 pub type ReportHandlerOpts = miette::MietteHandlerOpts;
15 #[cfg(feature = "fancy-diagnostics")]
16 pub type DefaultReportHandler = miette::GraphicalReportHandler;
17 #[cfg(not(feature = "fancy-diagnostics"))]
18 pub type DefaultReportHandler = miette::DebugReportHandler;
19
20 pub struct PrintDiagnostic<D, R = DefaultReportHandler> {
21 handler: R,
22 diag: D,
23 }
24 impl<D: AsRef<dyn super::Diagnostic>> PrintDiagnostic<D> {
25 pub fn new(diag: D) -> Self {
26 Self {
27 handler: Default::default(),
28 diag,
29 }
30 }
31 #[cfg(feature = "fancy-diagnostics")]
32 pub fn new_without_color(diag: D) -> Self {
33 Self {
34 handler: DefaultReportHandler::new_themed(GraphicalTheme::none()),
35 diag,
36 }
37 }
38 #[cfg(not(feature = "fancy-diagnostics"))]
39 pub fn new_without_color(diag: D) -> Self {
40 Self::new(diag)
41 }
42 }
43 impl<D: AsRef<dyn super::Diagnostic>> PrintDiagnostic<D, NarratableReportHandler> {
44 pub fn narrated(diag: D) -> Self {
45 Self {
46 handler: NarratableReportHandler::default(),
47 diag,
48 }
49 }
50 }
51 impl<D: AsRef<dyn super::Diagnostic>> PrintDiagnostic<D, JSONReportHandler> {
52 pub fn json(diag: D) -> Self {
53 Self {
54 handler: JSONReportHandler,
55 diag,
56 }
57 }
58 }
59 impl<D: AsRef<dyn super::Diagnostic>> core::fmt::Display for PrintDiagnostic<D> {
60 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
61 self.handler.render_report(f, self.diag.as_ref())
62 }
63 }
64 impl<D: AsRef<dyn super::Diagnostic>> core::fmt::Display
65 for PrintDiagnostic<D, NarratableReportHandler>
66 {
67 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
68 self.handler.render_report(f, self.diag.as_ref())
69 }
70 }
71 impl<D: AsRef<dyn super::Diagnostic>> core::fmt::Display for PrintDiagnostic<D, JSONReportHandler> {
72 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
73 self.handler.render_report(f, self.diag.as_ref())
74 }
75 }
76}
77
78#[cfg(feature = "fancy-diagnostics")]
79pub use miette::set_panic_hook;
80
81pub type Diag = miette::MietteDiagnostic;
82pub type DiagResult<T> = miette::Result<T>;
83
84use std::{
85 borrow::{Borrow, Cow},
86 fmt,
87 hash::{Hash, Hasher},
88 ops::{Deref, DerefMut},
89 path::Path,
90 sync::Arc,
91};
92
93use miette::{MietteError, SpanContents};
94
95use crate::{range::Range, StaticCow};
96
97#[derive(Debug, Clone, PartialEq, Eq)]
98pub struct Label {
99 span: SourceSpan,
100 label: Option<StaticCow<str>>,
101}
102impl Label {
103 pub fn at<R>(range: R) -> Self
104 where
105 Range<usize>: From<R>,
106 {
107 let range = Range::<usize>::from(range);
108 Self {
109 span: range.into(),
110 label: None,
111 }
112 }
113
114 pub fn point<L>(at: usize, label: L) -> Self
115 where
116 StaticCow<str>: From<L>,
117 {
118 Self {
119 span: SourceSpan::from(at),
120 label: Some(Cow::from(label)),
121 }
122 }
123
124 pub fn new<R, L>(range: R, label: L) -> Self
125 where
126 Range<usize>: From<R>,
127 StaticCow<str>: From<L>,
128 {
129 let range = Range::<usize>::from(range);
130 Self {
131 span: range.into(),
132 label: Some(Cow::from(label)),
133 }
134 }
135
136 pub fn label(&self) -> Option<&str> {
137 self.label.as_deref()
138 }
139}
140impl From<Label> for SourceSpan {
141 #[inline(always)]
142 fn from(label: Label) -> SourceSpan {
143 label.span
144 }
145}
146impl From<Label> for LabeledSpan {
147 #[inline]
148 fn from(label: Label) -> LabeledSpan {
149 if let Some(message) = label.label {
150 LabeledSpan::at(label.span, message)
151 } else {
152 LabeledSpan::underline(label.span)
153 }
154 }
155}
156
157#[derive(Debug, Clone, PartialEq, Eq)]
158pub enum FileName {
159 Stdin,
160 Path(Box<Path>),
161 Virtual(StaticCow<str>),
162}
163impl FileName {
164 pub fn from_static_str(name: &'static str) -> Self {
165 Self::Virtual(Cow::Borrowed(name))
166 }
167
168 pub fn name(&self) -> &str {
169 match self {
170 Self::Stdin => "stdin",
171 Self::Path(ref path) => path.to_str().unwrap_or("<invalid>"),
172 Self::Virtual(ref name) => name.as_ref(),
173 }
174 }
175}
176impl From<&Path> for FileName {
177 fn from(path: &Path) -> Self {
178 if path.as_os_str() == "-" {
179 Self::Stdin
180 } else {
181 Self::Path(path.to_path_buf().into_boxed_path())
182 }
183 }
184}
185impl From<Box<Path>> for FileName {
186 fn from(path: Box<Path>) -> Self {
187 if path.as_os_str() == "-" {
188 Self::Stdin
189 } else {
190 Self::Path(path)
191 }
192 }
193}
194impl From<std::path::PathBuf> for FileName {
195 fn from(path: std::path::PathBuf) -> Self {
196 if path.as_os_str() == "-" {
197 Self::Stdin
198 } else {
199 Self::Path(path.into_boxed_path())
200 }
201 }
202}
203impl From<&str> for FileName {
204 fn from(name: &str) -> Self {
205 if name == "-" {
206 Self::Stdin
207 } else {
208 Self::Virtual(Cow::Owned(name.to_string()))
209 }
210 }
211}
212impl From<String> for FileName {
213 fn from(name: String) -> Self {
214 if name == "-" {
215 Self::Stdin
216 } else {
217 Self::Virtual(Cow::Owned(name))
218 }
219 }
220}
221impl fmt::Display for FileName {
222 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
223 match self {
224 Self::Stdin => f.write_str("stdin"),
225 Self::Path(ref path) => write!(f, "{}", path.display()),
226 Self::Virtual(ref name) => f.write_str(name),
227 }
228 }
229}
230
231pub trait NamedSourceFile: SourceFile {
232 fn name(&self) -> FileName {
233 FileName::Stdin
234 }
235}
236
237pub trait SourceFile {
238 fn span(&self) -> SourceSpan;
239 fn source(&self) -> &str;
240 #[inline]
241 fn source_bytes(&self) -> &[u8] {
242 self.source().as_bytes()
243 }
244}
245impl SourceFile for str {
246 fn span(&self) -> SourceSpan {
247 let len = self.as_bytes().len();
248 SourceSpan::from(0..len)
249 }
250 fn source(&self) -> &str {
251 self
252 }
253}
254impl NamedSourceFile for str {}
255impl SourceFile for String {
256 fn span(&self) -> SourceSpan {
257 let len = self.as_bytes().len();
258 SourceSpan::from(0..len)
259 }
260 fn source(&self) -> &str {
261 self.as_str()
262 }
263}
264impl NamedSourceFile for String {}
265impl SourceFile for Box<str> {
266 fn span(&self) -> SourceSpan {
267 (**self).span()
268 }
269 fn source(&self) -> &str {
270 self.as_ref()
271 }
272}
273impl NamedSourceFile for Box<str> {}
274impl<'a> SourceFile for Cow<'a, str> {
275 fn span(&self) -> SourceSpan {
276 (**self).span()
277 }
278 fn source(&self) -> &str {
279 self.as_ref()
280 }
281}
282impl<'a> NamedSourceFile for Cow<'a, str> {}
283impl SourceFile for Arc<str> {
284 fn span(&self) -> SourceSpan {
285 (**self).span()
286 }
287 fn source(&self) -> &str {
288 self.as_ref()
289 }
290}
291impl NamedSourceFile for Arc<str> {}
292
293#[derive(Debug, Clone)]
294pub struct ArcSource(Arc<Source<'static>>);
295impl From<String> for ArcSource {
296 fn from(s: String) -> Self {
297 Self::new(Source::from(s))
298 }
299}
300impl From<&'static str> for ArcSource {
301 fn from(s: &'static str) -> Self {
302 Self::new(Source::new(FileName::Stdin, Cow::Borrowed(s)))
303 }
304}
305impl From<Source<'static>> for ArcSource {
306 fn from(source: Source<'static>) -> Self {
307 Self::new(source)
308 }
309}
310impl ArcSource {
311 pub fn new(source: Source<'static>) -> Self {
312 Self(Arc::new(source))
313 }
314}
315impl Deref for ArcSource {
316 type Target = str;
317
318 fn deref(&self) -> &Self::Target {
319 self.0.source()
320 }
321}
322impl AsRef<[u8]> for ArcSource {
323 #[inline(always)]
324 fn as_ref(&self) -> &[u8] {
325 self.0.source_bytes()
326 }
327}
328impl AsRef<str> for ArcSource {
329 #[inline(always)]
330 fn as_ref(&self) -> &str {
331 self.0.source()
332 }
333}
334impl SourceFile for ArcSource {
335 fn span(&self) -> SourceSpan {
336 self.0.span()
337 }
338 fn source(&self) -> &str {
339 self.0.source()
340 }
341}
342impl NamedSourceFile for ArcSource {
343 fn name(&self) -> FileName {
344 self.0.name()
345 }
346}
347impl SourceCode for ArcSource {
348 #[inline(always)]
349 fn read_span<'a>(
350 &'a self,
351 span: &SourceSpan,
352 context_lines_before: usize,
353 context_lines_after: usize,
354 ) -> Result<Box<dyn SpanContents<'a> + 'a>, MietteError> {
355 self.0
356 .read_span(span, context_lines_before, context_lines_after)
357 }
358}
359
360#[derive(Debug)]
361pub struct Source<'a> {
362 pub name: FileName,
363 pub code: Cow<'a, str>,
364}
365impl<'a> From<&'a str> for Source<'a> {
366 fn from(s: &'a str) -> Self {
367 Self {
368 name: FileName::Stdin,
369 code: Cow::Borrowed(s),
370 }
371 }
372}
373impl<'a> From<String> for Source<'a> {
374 fn from(code: String) -> Self {
375 Self {
376 name: FileName::Stdin,
377 code: Cow::Owned(code),
378 }
379 }
380}
381impl<'a> Source<'a> {
382 pub fn new<N, S>(name: N, code: S) -> Self
383 where
384 FileName: From<N>,
385 Cow<'a, str>: From<S>,
386 {
387 Self {
388 name: FileName::from(name),
389 code: Cow::from(code),
390 }
391 }
392}
393impl Source<'static> {
394 pub fn into_arc_source(self) -> ArcSource {
395 ArcSource::new(self)
396 }
397}
398impl<'a> SourceFile for Source<'a> {
399 fn span(&self) -> SourceSpan {
400 self.code.span()
401 }
402 fn source(&self) -> &str {
403 self.code.source()
404 }
405}
406impl<'a> NamedSourceFile for Source<'a> {
407 fn name(&self) -> FileName {
408 self.name.clone()
409 }
410}
411impl<'s> SourceCode for Source<'s> {
412 #[inline(always)]
413 fn read_span<'a>(
414 &'a self,
415 span: &SourceSpan,
416 context_lines_before: usize,
417 context_lines_after: usize,
418 ) -> Result<Box<dyn SpanContents<'a> + 'a>, MietteError> {
419 self.code
420 .read_span(span, context_lines_before, context_lines_after)
421 }
422}
423
424pub trait Spanned {
425 fn start(&self) -> usize {
426 self.span().start()
427 }
428 fn end(&self) -> usize {
429 self.span().end()
430 }
431 fn range(&self) -> Range<usize> {
432 self.span().range()
433 }
434 fn span(&self) -> SourceSpan;
435}
436impl<S: Spanned + ?Sized> Spanned for Box<S> {
437 fn span(&self) -> SourceSpan {
438 (**self).span()
439 }
440}
441impl<S: Spanned + ?Sized> Spanned for &S {
442 fn span(&self) -> SourceSpan {
443 (**self).span()
444 }
445}
446impl<S: Spanned + ?Sized> Spanned for &mut S {
447 fn span(&self) -> SourceSpan {
448 (**self).span()
449 }
450}
451impl Spanned for SourceSpan {
452 #[inline(always)]
453 fn start(&self) -> usize {
454 self.offset()
455 }
456 #[inline(always)]
457 fn end(&self) -> usize {
458 self.offset() + self.len()
459 }
460 #[inline]
461 fn range(&self) -> Range<usize> {
462 let offset = self.offset();
463 Range::new(offset, offset + self.len())
464 }
465 fn span(&self) -> SourceSpan {
466 *self
467 }
468}
469
470pub struct Span<T> {
472 span: Range<usize>,
473 spanned: T,
474}
475impl<T: Copy> Copy for Span<T> {}
476impl<T: Clone> Clone for Span<T> {
477 fn clone(&self) -> Self {
478 Self {
479 span: self.span,
480 spanned: self.spanned.clone(),
481 }
482 }
483}
484impl<T> Span<T> {
485 #[inline]
487 pub fn new<R>(range: R, spanned: T) -> Self
488 where
489 Range<usize>: From<R>,
490 {
491 Self {
492 span: Range::from(range),
493 spanned,
494 }
495 }
496
497 #[inline]
499 pub fn at(offset: usize, spanned: T) -> Self {
500 Self {
501 span: Range::new(offset, offset),
502 spanned,
503 }
504 }
505
506 #[inline]
507 pub fn map<U, F>(self, mut f: F) -> Span<U>
508 where
509 F: FnMut(T) -> U,
510 {
511 Span {
512 span: self.span,
513 spanned: f(self.spanned),
514 }
515 }
516
517 #[inline]
519 pub fn shift(&mut self, count: usize) {
520 self.span.start += count;
521 self.span.end += count;
522 }
523
524 #[inline]
526 pub fn extend(&mut self, count: usize) {
527 self.span.end += count;
528 }
529
530 #[inline]
531 pub fn into_parts(self) -> (SourceSpan, T) {
532 (self.span.into(), self.spanned)
533 }
534
535 #[inline]
536 pub fn into_inner(self) -> T {
537 self.spanned
538 }
539}
540impl<T: Borrow<str>, S: Borrow<T>> Borrow<T> for Span<S> {
541 fn borrow(&self) -> &T {
542 self.spanned.borrow()
543 }
544}
545impl<T> Deref for Span<T> {
546 type Target = T;
547
548 #[inline(always)]
549 fn deref(&self) -> &Self::Target {
550 &self.spanned
551 }
552}
553impl<T> DerefMut for Span<T> {
554 #[inline(always)]
555 fn deref_mut(&mut self) -> &mut Self::Target {
556 &mut self.spanned
557 }
558}
559impl<T: ?Sized, U: AsRef<T>> AsRef<T> for Span<U> {
560 fn as_ref(&self) -> &T {
561 self.spanned.as_ref()
562 }
563}
564impl<T: ?Sized, U: AsMut<T>> AsMut<T> for Span<U> {
565 fn as_mut(&mut self) -> &mut T {
566 self.spanned.as_mut()
567 }
568}
569impl<T: fmt::Debug> fmt::Debug for Span<T> {
570 #[inline]
571 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
572 fmt::Debug::fmt(&self.spanned, f)
573 }
574}
575impl<T: fmt::Display> fmt::Display for Span<T> {
576 #[inline]
577 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
578 fmt::Display::fmt(&self.spanned, f)
579 }
580}
581impl<T: Eq> Eq for Span<T> {}
582impl<T: PartialEq> PartialEq for Span<T> {
583 #[inline]
584 fn eq(&self, other: &Self) -> bool {
585 self.spanned.eq(&other.spanned)
586 }
587}
588impl<T: PartialEq> PartialEq<T> for Span<T> {
589 #[inline]
590 fn eq(&self, other: &T) -> bool {
591 self.spanned.eq(other)
592 }
593}
594impl<T: Ord> Ord for Span<T> {
595 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
596 self.spanned.cmp(&other.spanned)
597 }
598}
599impl<T: PartialOrd> PartialOrd for Span<T> {
600 #[inline]
601 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
602 self.spanned.partial_cmp(&other.spanned)
603 }
604}
605impl<T: Hash> Hash for Span<T> {
606 fn hash<H: Hasher>(&self, state: &mut H) {
607 self.spanned.hash(state);
608 }
609}
610impl<T> Spanned for Span<T> {
611 #[inline(always)]
612 fn start(&self) -> usize {
613 self.span.start
614 }
615
616 #[inline(always)]
617 fn end(&self) -> usize {
618 self.span.end
619 }
620
621 #[inline(always)]
622 fn range(&self) -> Range<usize> {
623 self.span
624 }
625
626 #[inline]
627 fn span(&self) -> SourceSpan {
628 self.span.into()
629 }
630}