1use std::{
7 borrow::{Borrow, Cow},
8 error::Error,
9 fmt::{self, Display},
10 fs, mem,
11 ops::{Deref, Range},
12 panic::Location,
13};
14
15#[cfg(feature = "serde")]
16use serde::{Deserialize, Serialize};
17
18use crate::MietteError;
19
20pub trait Diagnostic: Error {
24 fn code(&self) -> Option<Cow<'_, str>> {
30 None
31 }
32
33 fn severity(&self) -> Option<Severity> {
39 None
40 }
41
42 fn help(&self) -> Option<Cow<'_, str>> {
45 None
46 }
47
48 fn note(&self) -> Option<Cow<'_, str>> {
52 None
53 }
54
55 fn url(&self) -> Option<Cow<'_, str>> {
58 None
59 }
60
61 fn source_code(&self) -> Option<&dyn SourceCode> {
63 None
64 }
65
66 fn labels(&self) -> crate::Labels {
72 crate::Labels::None
73 }
74
75 fn related(&self) -> Related<'_> {
81 Related::None
82 }
83
84 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
86 None
87 }
88}
89
90#[derive(Default)]
95pub enum Related<'a> {
96 #[default]
98 None,
99 One([&'a dyn Diagnostic; 1]),
101 Two([&'a dyn Diagnostic; 2]),
103 Many(Vec<&'a dyn Diagnostic>),
105}
106
107impl<'a> Related<'a> {
108 #[must_use]
110 pub fn as_slice(&self) -> &[&'a dyn Diagnostic] {
111 match self {
112 Related::None => &[],
113 Related::One(related) => related,
114 Related::Two(related) => related,
115 Related::Many(related) => related,
116 }
117 }
118
119 #[must_use]
121 pub fn is_empty(&self) -> bool {
122 matches!(self, Related::None)
123 }
124
125 #[must_use]
127 pub fn len(&self) -> usize {
128 self.as_slice().len()
129 }
130
131 pub fn push(&mut self, related: &'a dyn Diagnostic) {
133 if let Related::Many(items) = self {
134 items.push(related);
135 return;
136 }
137 *self = match mem::take(self) {
138 Related::None => Related::One([related]),
139 Related::One([a]) => Related::Two([a, related]),
140 Related::Two([a, b]) => Related::Many(vec![a, b, related]),
141 Related::Many(_) => unreachable!("handled by the fast path above"),
142 };
143 }
144}
145
146impl<'a> Deref for Related<'a> {
147 type Target = [&'a dyn Diagnostic];
148
149 fn deref(&self) -> &Self::Target {
150 self.as_slice()
151 }
152}
153
154impl<'a> Extend<&'a dyn Diagnostic> for Related<'a> {
155 fn extend<I: IntoIterator<Item = &'a dyn Diagnostic>>(&mut self, iter: I) {
156 let mut iter = iter.into_iter();
157 while !matches!(self, Related::Many(_)) {
158 match iter.next() {
159 Some(related) => self.push(related),
160 None => return,
161 }
162 }
163 if let Related::Many(items) = self {
164 items.reserve(iter.size_hint().0);
165 items.extend(iter);
166 }
167 }
168}
169
170impl<'a> FromIterator<&'a dyn Diagnostic> for Related<'a> {
171 fn from_iter<I: IntoIterator<Item = &'a dyn Diagnostic>>(iter: I) -> Self {
172 let mut iter = iter.into_iter();
173 if iter.size_hint().0 > 2 {
174 return Related::Many(iter.collect());
175 }
176 let Some(a) = iter.next() else { return Related::None };
177 let Some(b) = iter.next() else { return Related::One([a]) };
178 let Some(c) = iter.next() else { return Related::Two([a, b]) };
179 let mut items = Vec::with_capacity(3 + iter.size_hint().0);
180 items.extend([a, b, c]);
181 items.extend(iter);
182 Related::Many(items)
183 }
184}
185
186macro_rules! box_error_impls {
187 ($($box_type:ty),*) => {
188 $(
189 impl Error for $box_type {
190 fn source(&self) -> Option<&(dyn Error + 'static)> {
191 (**self).source()
192 }
193
194 fn cause(&self) -> Option<&dyn Error> {
195 self.source()
196 }
197 }
198 )*
199 }
200}
201
202box_error_impls! {
203 Box<dyn Diagnostic>,
204 Box<dyn Diagnostic + Send>,
205 Box<dyn Diagnostic + Send + Sync>
206}
207
208macro_rules! box_borrow_impls {
209 ($($box_type:ty),*) => {
210 $(
211 impl Borrow<dyn Diagnostic> for $box_type {
212 fn borrow(&self) -> &(dyn Diagnostic + 'static) {
213 self.as_ref()
214 }
215 }
216 )*
217 }
218}
219
220box_borrow_impls! {
221 Box<dyn Diagnostic + Send>,
222 Box<dyn Diagnostic + Send + Sync>
223}
224
225impl<T: Diagnostic + Send + Sync + 'static> From<T>
226 for Box<dyn Diagnostic + Send + Sync + 'static>
227{
228 fn from(diag: T) -> Self {
229 Box::new(diag)
230 }
231}
232
233impl<T: Diagnostic + Send + Sync + 'static> From<T> for Box<dyn Diagnostic + Send + 'static> {
234 fn from(diag: T) -> Self {
235 Box::<dyn Diagnostic + Send + Sync>::from(diag)
236 }
237}
238
239impl<T: Diagnostic + Send + Sync + 'static> From<T> for Box<dyn Diagnostic + 'static> {
240 fn from(diag: T) -> Self {
241 Box::<dyn Diagnostic + Send + Sync>::from(diag)
242 }
243}
244
245impl From<&str> for Box<dyn Diagnostic> {
246 fn from(s: &str) -> Self {
247 From::from(String::from(s))
248 }
249}
250
251impl From<&str> for Box<dyn Diagnostic + Send + Sync + '_> {
252 fn from(s: &str) -> Self {
253 From::from(String::from(s))
254 }
255}
256
257impl From<String> for Box<dyn Diagnostic> {
258 fn from(s: String) -> Self {
259 let err1: Box<dyn Diagnostic + Send + Sync> = From::from(s);
260 let err2: Box<dyn Diagnostic> = err1;
261 err2
262 }
263}
264
265impl From<String> for Box<dyn Diagnostic + Send + Sync> {
266 fn from(s: String) -> Self {
267 struct StringError(String);
268
269 impl Error for StringError {}
270 impl Diagnostic for StringError {}
271
272 impl Display for StringError {
273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274 Display::fmt(&self.0, f)
275 }
276 }
277
278 impl fmt::Debug for StringError {
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 fmt::Debug::fmt(&self.0, f)
282 }
283 }
284
285 Box::new(StringError(s))
286 }
287}
288
289impl From<Box<dyn Error + Send + Sync>> for Box<dyn Diagnostic + Send + Sync> {
290 fn from(s: Box<dyn Error + Send + Sync>) -> Self {
291 #[derive(thiserror::Error)]
292 #[error(transparent)]
293 struct BoxedDiagnostic(Box<dyn Error + Send + Sync>);
294 impl fmt::Debug for BoxedDiagnostic {
295 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296 fmt::Debug::fmt(&self.0, f)
297 }
298 }
299
300 impl Diagnostic for BoxedDiagnostic {}
301
302 Box::new(BoxedDiagnostic(s))
303 }
304}
305
306#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
312#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
313#[derive(Default)]
314pub enum Severity {
315 Advice,
317 Warning,
319 #[default]
322 Error,
323}
324
325#[cfg(feature = "serde")]
326#[test]
327fn test_serialize_severity() {
328 use serde_json::json;
329
330 assert_eq!(json!(Severity::Advice), json!("Advice"));
331 assert_eq!(json!(Severity::Warning), json!("Warning"));
332 assert_eq!(json!(Severity::Error), json!("Error"));
333}
334
335#[cfg(feature = "serde")]
336#[test]
337fn test_deserialize_severity() {
338 use serde_json::json;
339
340 let severity: Severity = serde_json::from_value(json!("Advice")).unwrap();
341 assert_eq!(severity, Severity::Advice);
342
343 let severity: Severity = serde_json::from_value(json!("Warning")).unwrap();
344 assert_eq!(severity, Severity::Warning);
345
346 let severity: Severity = serde_json::from_value(json!("Error")).unwrap();
347 assert_eq!(severity, Severity::Error);
348}
349
350pub trait SourceCode: Send + Sync {
362 fn read_span<'a>(
365 &'a self,
366 span: &SourceSpan,
367 context_lines_before: usize,
368 context_lines_after: usize,
369 ) -> Result<MietteSpanContents<'a>, MietteError>;
370
371 fn name(&self) -> Option<&str> {
373 None
374 }
375}
376
377#[derive(Debug, Clone, PartialEq, Eq)]
379#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
380pub struct LabeledSpan {
381 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
382 label: Option<String>,
383 span: SourceSpan,
384 primary: bool,
385}
386
387impl LabeledSpan {
388 #[must_use]
390 pub const fn new(label: Option<String>, offset: ByteOffset, len: u32) -> Self {
391 Self { label, span: SourceSpan::new(SourceOffset(offset), len), primary: false }
392 }
393
394 #[must_use]
396 pub fn new_with_span(label: Option<String>, span: impl Into<SourceSpan>) -> Self {
397 Self { label, span: span.into(), primary: false }
398 }
399
400 #[must_use]
402 pub fn new_primary_with_span(label: Option<String>, span: impl Into<SourceSpan>) -> Self {
403 Self { label, span: span.into(), primary: true }
404 }
405
406 pub fn set_label(&mut self, label: Option<String>) {
408 self.label = label;
409 }
410
411 pub fn set_span_offset(&mut self, offset: ByteOffset) {
413 self.span.offset = SourceOffset(offset);
414 }
415
416 #[must_use]
430 pub fn at(span: impl Into<SourceSpan>, label: impl Into<String>) -> Self {
431 Self::new_with_span(Some(label.into()), span)
432 }
433
434 pub fn at_offset(offset: ByteOffset, label: impl Into<String>) -> Self {
448 Self::new(Some(label.into()), offset, 0)
449 }
450
451 #[must_use]
462 pub fn underline(span: impl Into<SourceSpan>) -> Self {
463 Self::new_with_span(None, span)
464 }
465
466 pub fn label(&self) -> Option<&str> {
468 self.label.as_deref()
469 }
470
471 pub const fn inner(&self) -> &SourceSpan {
473 &self.span
474 }
475
476 pub const fn offset(&self) -> ByteOffset {
478 self.span.offset()
479 }
480
481 pub const fn len(&self) -> u32 {
483 self.span.len()
484 }
485
486 pub const fn is_empty(&self) -> bool {
488 self.span.is_empty()
489 }
490
491 pub const fn primary(&self) -> bool {
493 self.primary
494 }
495}
496
497#[test]
498fn test_set_span_offset() {
499 let mut span = LabeledSpan::new(None, 10, 10);
500 assert_eq!(span.offset(), 10);
501
502 span.set_span_offset(20);
503 assert_eq!(span.offset(), 20);
504}
505
506#[cfg(feature = "serde")]
507#[test]
508fn test_serialize_labeled_span() {
509 use serde_json::json;
510
511 assert_eq!(
512 json!(LabeledSpan::new(None, 0, 0)),
513 json!({
514 "span": { "offset": 0, "length": 0, },
515 "primary": false,
516 })
517 );
518
519 assert_eq!(
520 json!(LabeledSpan::new(Some("label".to_string()), 0, 0)),
521 json!({
522 "label": "label",
523 "span": { "offset": 0, "length": 0, },
524 "primary": false,
525 })
526 );
527}
528
529#[cfg(feature = "serde")]
530#[test]
531fn test_deserialize_labeled_span() {
532 use serde_json::json;
533
534 let span: LabeledSpan = serde_json::from_value(json!({
535 "label": null,
536 "span": { "offset": 0, "length": 0, },
537 "primary": false,
538 }))
539 .unwrap();
540 assert_eq!(span, LabeledSpan::new(None, 0, 0));
541
542 let span: LabeledSpan = serde_json::from_value(json!({
543 "span": { "offset": 0, "length": 0, },
544 "primary": false
545 }))
546 .unwrap();
547 assert_eq!(span, LabeledSpan::new(None, 0, 0));
548
549 let span: LabeledSpan = serde_json::from_value(json!({
550 "label": "label",
551 "span": { "offset": 0, "length": 0, },
552 "primary": false
553 }))
554 .unwrap();
555 assert_eq!(span, LabeledSpan::new(Some("label".to_string()), 0, 0));
556}
557
558pub trait SpanContents<'a> {
564 fn data(&self) -> &'a [u8];
566 fn span(&self) -> &SourceSpan;
568 fn name(&self) -> Option<&str> {
570 None
571 }
572 fn line(&self) -> usize;
575 fn column(&self) -> usize;
578 fn line_count(&self) -> usize;
580
581 fn language(&self) -> Option<&str> {
587 None
588 }
589}
590
591#[derive(Clone, Debug)]
595pub struct MietteSpanContents<'a> {
596 data: &'a [u8],
598 span: SourceSpan,
600 line: usize,
602 column: usize,
604 line_count: usize,
606 name: Option<Cow<'a, str>>,
608 language: Option<Cow<'a, str>>,
610}
611
612impl<'a> MietteSpanContents<'a> {
613 pub const fn new(
615 data: &'a [u8],
616 span: SourceSpan,
617 line: usize,
618 column: usize,
619 line_count: usize,
620 ) -> MietteSpanContents<'a> {
621 MietteSpanContents { data, span, line, column, line_count, name: None, language: None }
622 }
623
624 pub const fn new_named(
626 name: Cow<'a, str>,
627 data: &'a [u8],
628 span: SourceSpan,
629 line: usize,
630 column: usize,
631 line_count: usize,
632 ) -> MietteSpanContents<'a> {
633 MietteSpanContents {
634 data,
635 span,
636 line,
637 column,
638 line_count,
639 name: Some(name),
640 language: None,
641 }
642 }
643
644 #[must_use]
646 pub fn with_language(mut self, language: impl Into<Cow<'a, str>>) -> Self {
647 self.language = Some(language.into());
648 self
649 }
650}
651
652impl<'a> SpanContents<'a> for MietteSpanContents<'a> {
653 fn data(&self) -> &'a [u8] {
654 self.data
655 }
656
657 fn span(&self) -> &SourceSpan {
658 &self.span
659 }
660
661 fn line(&self) -> usize {
662 self.line
663 }
664
665 fn column(&self) -> usize {
666 self.column
667 }
668
669 fn line_count(&self) -> usize {
670 self.line_count
671 }
672
673 fn name(&self) -> Option<&str> {
674 self.name.as_deref()
675 }
676
677 fn language(&self) -> Option<&str> {
678 self.language.as_deref()
679 }
680}
681
682#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
684#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
685pub struct SourceSpan {
686 offset: SourceOffset,
688 length: u32,
690}
691
692impl SourceSpan {
693 pub const fn new(start: SourceOffset, length: u32) -> Self {
695 Self { offset: start, length }
696 }
697
698 pub const fn offset(&self) -> ByteOffset {
700 self.offset.offset()
701 }
702
703 pub const fn len(&self) -> u32 {
705 self.length
706 }
707
708 pub const fn is_empty(&self) -> bool {
711 self.length == 0
712 }
713}
714
715impl From<(ByteOffset, u32)> for SourceSpan {
716 fn from((start, len): (ByteOffset, u32)) -> Self {
717 Self { offset: start.into(), length: len }
718 }
719}
720
721impl From<(SourceOffset, u32)> for SourceSpan {
722 fn from((start, len): (SourceOffset, u32)) -> Self {
723 Self::new(start, len)
724 }
725}
726
727impl From<Range<ByteOffset>> for SourceSpan {
728 fn from(range: Range<ByteOffset>) -> Self {
729 Self { offset: range.start.into(), length: range.len() as u32 }
732 }
733}
734
735impl From<SourceOffset> for SourceSpan {
736 fn from(offset: SourceOffset) -> Self {
737 Self { offset, length: 0 }
738 }
739}
740
741impl From<ByteOffset> for SourceSpan {
742 fn from(offset: ByteOffset) -> Self {
743 Self { offset: offset.into(), length: 0 }
744 }
745}
746
747#[cfg(feature = "serde")]
748#[test]
749fn test_serialize_source_span() {
750 use serde_json::json;
751
752 assert_eq!(json!(SourceSpan::from(0)), json!({ "offset": 0, "length": 0}));
753}
754
755#[cfg(feature = "serde")]
756#[test]
757fn test_deserialize_source_span() {
758 use serde_json::json;
759
760 let span: SourceSpan = serde_json::from_value(json!({ "offset": 0, "length": 0})).unwrap();
761 assert_eq!(span, SourceSpan::from(0));
762}
763
764pub type ByteOffset = u32;
768
769#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
773#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
774pub struct SourceOffset(ByteOffset);
775
776impl SourceOffset {
777 pub const fn offset(&self) -> ByteOffset {
779 self.0
780 }
781
782 pub fn from_location(source: impl AsRef<str>, loc_line: usize, loc_col: usize) -> Self {
788 let mut line = 0usize;
789 let mut col = 0usize;
790 let mut offset = 0usize;
791 for char in source.as_ref().chars() {
792 if line + 1 >= loc_line && col + 1 >= loc_col {
793 break;
794 }
795 if char == '\n' {
796 col = 0;
797 line += 1;
798 } else {
799 col += 1;
800 }
801 offset += char.len_utf8();
802 }
803
804 SourceOffset(offset as u32)
805 }
806
807 #[track_caller]
819 pub fn from_current_location() -> Result<(String, Self), MietteError> {
820 let loc = Location::caller();
821 Ok((
822 loc.file().into(),
823 fs::read_to_string(loc.file())
824 .map(|txt| Self::from_location(txt, loc.line() as usize, loc.column() as usize))?,
825 ))
826 }
827}
828
829impl From<ByteOffset> for SourceOffset {
830 fn from(bytes: ByteOffset) -> Self {
831 SourceOffset(bytes)
832 }
833}
834
835#[test]
836fn test_source_offset_from_location() {
837 let source = "f\n\noo\r\nbar";
838
839 assert_eq!(SourceOffset::from_location(source, 1, 1).offset(), 0);
840 assert_eq!(SourceOffset::from_location(source, 1, 2).offset(), 1);
841 assert_eq!(SourceOffset::from_location(source, 2, 1).offset(), 2);
842 assert_eq!(SourceOffset::from_location(source, 3, 1).offset(), 3);
843 assert_eq!(SourceOffset::from_location(source, 3, 2).offset(), 4);
844 assert_eq!(SourceOffset::from_location(source, 3, 3).offset(), 5);
845 assert_eq!(SourceOffset::from_location(source, 3, 4).offset(), 6);
846 assert_eq!(SourceOffset::from_location(source, 4, 1).offset(), 7);
847 assert_eq!(SourceOffset::from_location(source, 4, 2).offset(), 8);
848 assert_eq!(SourceOffset::from_location(source, 4, 3).offset(), 9);
849 assert_eq!(SourceOffset::from_location(source, 4, 4).offset(), 10);
850
851 assert_eq!(SourceOffset::from_location(source, 5, 1).offset(), source.len() as u32);
853}
854
855#[cfg(feature = "serde")]
856#[test]
857fn test_serialize_source_offset() {
858 use serde_json::json;
859
860 assert_eq!(json!(SourceOffset::from(0)), 0);
861}
862
863#[cfg(feature = "serde")]
864#[test]
865fn test_deserialize_source_offset() {
866 let offset: SourceOffset = serde_json::from_str("0").unwrap();
867 assert_eq!(offset, SourceOffset::from(0));
868}