Skip to main content

huff_neo_utils/
report.rs

1use std::{
2    fmt,
3    io::{self, Write},
4};
5
6use crate::file::span::{Span, Spanned};
7
8/// A Report Trait
9pub trait Report<W>: Spanned {
10    /// Report to the reporter
11    fn report(&self, reporter: &mut Reporter<'_, W>) -> io::Result<()>;
12}
13
14/// A Reporter
15pub struct Reporter<'a, W> {
16    /// The output writer
17    pub out: W,
18    /// The raw source code
19    pub source: &'a str,
20}
21
22impl<'a, W> Reporter<'a, W>
23where
24    W: Write,
25{
26    /// Public associated function to instatiate a new Reporter.
27    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    /// Reports and exits the process.
42    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}