1use std::{
2 fmt,
3 io::{self, Write},
4};
5
6use crate::file::span::{Span, Spanned};
7
8pub trait Report<W>: Spanned {
10 fn report(&self, reporter: &mut Reporter<'_, W>) -> io::Result<()>;
12}
13
14pub struct Reporter<'a, W> {
16 pub out: W,
18 pub source: &'a str,
20}
21
22impl<'a, W> Reporter<'a, W>
23where
24 W: Write,
25{
26 pub fn new(out: W, source: &'a str) -> Self {
28 Self { out, source }
29 }
30
31 fn report(&mut self, err: impl Report<W>) -> Result<(), io::Error> {
32 write!(self.out, "[error]: ")?;
33 err.report(self)?;
34 writeln!(self.out, "\n{}", self.source)?;
35
36 let pad = if err.span() == Span::EOF { self.source.len() } else { err.span().start };
37
38 writeln!(self.out, "{:pad$}^ ", "")
39 }
40
41 pub fn exit(&mut self, err: impl Report<W>) -> ! {
43 self.report(err).expect("failed to write to stdout");
44 std::process::exit(1)
45 }
46}
47
48impl<E, W> From<E> for Box<dyn Report<W>>
49where
50 E: Report<W> + fmt::Debug + 'static,
51{
52 fn from(err: E) -> Self {
53 Box::new(err)
54 }
55}
56
57impl<W> Spanned for Box<dyn Report<W>> {
58 fn span(&self) -> Span {
59 (**self).span()
60 }
61}
62
63impl<W> Report<W> for Box<dyn Report<W>> {
64 fn report(&self, reporter: &mut Reporter<'_, W>) -> io::Result<()> {
65 (**self).report(reporter)
66 }
67}