error_enum_core/
ariadne_impl.rs

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