about summary refs log tree commit diff
path: root/src/error.rs
blob: 227a4b04384d3d2e78fc6235a88276dbacd61004 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use crate::{interpret::walker::WalkerError, lex::token::Location, parse::parser::ParserError};
use colored::Colorize;
use std::fmt::Display;

pub struct RHError {
    pub at: ErrorLocation,
    pub kind: RHErrorKind,
}

impl RHError {
    pub fn hydrate_source<'s>(self, source: &'s str) -> RHErrorWithSource<'s> {
        RHErrorWithSource {
            error: self,
            source,
        }
    }
}

pub enum RHErrorKind {
    Parse(ParserError),
    Run(WalkerError),
}

pub enum ErrorLocation {
    Specific(Location),
    Eof,
}

pub struct RHErrorWithSource<'source> {
    error: RHError,
    source: &'source str,
}

impl Display for RHErrorWithSource<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let (error_title, error_string) = match &self.error.kind {
            RHErrorKind::Parse(parser_error) => (
                "Encountered error during parsing",
                format!("{}", parser_error),
            ),
            RHErrorKind::Run(walker_error) => {
                ("Encountered runtime error", format!("{}", walker_error))
            }
        };

        let weird_source_error_msg =
            "Found error but could not get it's location in source, how does this even happen?";

        let (line_number, line_column, source_line) = match self.error.at {
            ErrorLocation::Specific(location) => (
                location.row,
                location.col,
                self.source
                    .lines()
                    .nth(location.row)
                    .expect(weird_source_error_msg),
            ),
            ErrorLocation::Eof => {
                let lines = self.source.lines();
                let (line_count, _) = lines.size_hint();
                let last_line = lines.last().expect(weird_source_error_msg);

                (line_count, last_line.len(), last_line)
            }
        };

        let arrow_pad_len = line_column + 3 + line_number.to_string().len();

        writeln!(f, "{} {}:\n", " Error ".on_red(), error_title)?;
        writeln!(
            f,
            "{}{}",
            format!("{} | ", line_number).dimmed(),
            source_line
        )?;
        writeln!(f, "{}{}", " ".repeat(arrow_pad_len), "^".red())?;
        writeln!(f, "{}", error_string)?;

        Ok(())
    }
}