Skip to main content

error_enum_core/
ariadne_impl.rs

1use crate::{ErrorType, Kind, Span};
2use alloc::{string::String, vec::Vec};
3use ariadne::{Config, Label, Report, ReportKind};
4use core::{fmt, iter};
5use std::io;
6
7impl From<Kind> for ReportKind<'_> {
8    fn from(kind: Kind) -> Self {
9        match kind {
10            Kind::Error => ReportKind::Error,
11            Kind::Warn => ReportKind::Warning,
12        }
13    }
14}
15
16pub(crate) struct SpanWrapper<T>(T);
17
18impl<T: Span> ariadne::Span for SpanWrapper<T> {
19    type SourceId = T::Uri;
20
21    fn source(&self) -> &Self::SourceId {
22        self.0.uri()
23    }
24    fn start(&self) -> usize {
25        Span::start(&self.0)
26    }
27    fn end(&self) -> usize {
28        Span::end(&self.0)
29    }
30}
31
32type SourceEntry<T> = (
33    <<T as ErrorType>::Span as Span>::Uri,
34    ariadne::Source<<<T as ErrorType>::Span as Span>::Source>,
35);
36
37struct Cache<T: ErrorType + ?Sized> {
38    sources: Vec<SourceEntry<T>>,
39}
40
41impl<T: ErrorType + ?Sized> FromIterator<T::Span> for Cache<T> {
42    fn from_iter<I: IntoIterator<Item = T::Span>>(iter: I) -> Self {
43        let sources = iter
44            .into_iter()
45            .map(
46                |span| -> (
47                    <T::Span as Span>::Uri,
48                    ariadne::Source<<T::Span as Span>::Source>,
49                ) {
50                    (
51                        span.uri().clone(),
52                        ariadne::Source::from(span.source_text().clone()),
53                    )
54                },
55            )
56            .collect();
57        Self { sources }
58    }
59}
60
61impl<T: ErrorType + ?Sized> ariadne::Cache<<T::Span as Span>::Uri> for Cache<T> {
62    type Storage = <T::Span as Span>::Source;
63
64    fn fetch(
65        &mut self,
66        id: &<T::Span as Span>::Uri,
67    ) -> Result<&ariadne::Source<Self::Storage>, impl fmt::Debug> {
68        self.sources
69            .iter()
70            .find(|(uri, _)| uri == id)
71            .map(|(_, source)| source)
72            .ok_or("Source not found")
73    }
74
75    fn display<'a>(&self, id: &'a <T::Span as Span>::Uri) -> Option<impl fmt::Display + 'a> {
76        self.sources
77            .iter()
78            .find(|(uri, _)| uri == id)
79            .map(|(uri, _)| uri)
80            .cloned()
81    }
82}
83
84pub(crate) fn to_ariadne_report<T: ErrorType + ?Sized>(
85    error: &T,
86    buf: &mut impl io::Write,
87    config: Config,
88) -> Result<(), io::Error> {
89    let primary_span = error.primary_span().unwrap_or_default();
90    let primary_message = error.primary_message();
91    let cache: Cache<T> = Cache::from_iter(iter::once(primary_span.clone()));
92    Report::build(error.kind().into(), SpanWrapper(primary_span.clone()))
93        .with_code(error.code())
94        .with_message(primary_message)
95        .with_label(Label::new(SpanWrapper(primary_span)).with_message(error.primary_label()))
96        .with_config(config)
97        .finish()
98        .write(cache, buf)
99}
100pub(crate) fn fmt_as_ariadne_report<T: ErrorType + ?Sized>(
101    error: &T,
102    config: Config,
103) -> Result<String, io::Error> {
104    let mut result = Vec::new();
105    to_ariadne_report(error, &mut result, config)?;
106    String::from_utf8(result).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
107}