summaryrefslogtreecommitdiff
path: root/src/errors/mod.rs
blob: b0bf7e4d67f936114addaade4f9db0ecfcf2230c (plain) (blame)
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
mod file_error;
mod merge_error;
mod resolver_error;

pub use file_error::*;
pub use merge_error::*;
pub use resolver_error::*;

use crate::*;

use ansi::*;
use log::LogLevel;


pub fn report_source_issue(level: LogLevel, context: &Context, message: &str) {
    // Prepare variables.
    let in_merged = &context.source.in_merged;
    let line_num = in_merged.start.line + 1;
    let digits = line_num.to_string().len();
    let w = digits + 3;
    let arrow = "-->";
    let mut string = message.to_string();

    macro_rules! push {
        ($($tokens:tt)*) => { string.push_str(&format!($($tokens)*)) };
    }

    // Format message and locations.
    push!("{NORMAL}\n");
    push!("{BLUE}{arrow:>w$}{NORMAL} {in_merged}\n", w=w);
    if let Some(in_source) = &context.source.in_source {
        push!("{BLUE}{arrow:>w$}{NORMAL} {in_source}\n", w=w);
    }

    // Format source context.
    let left = in_merged.start.column;
    let right = in_merged.end.column + 1;
    let source_line = context.source_code.split('\n').nth(in_merged.start.line)
        .unwrap_or("<error reading line from source>");
    let space = " ";
    let colour = match level {
        LogLevel::Info => BLUE,
        LogLevel::Warn => YELLOW,
        LogLevel::Error => RED,
        LogLevel::Fatal => RED,
    };

    // Print source code line.
    push!("{BLUE} {line_num} | {NORMAL}");
    for (i, c) in source_line.chars().enumerate() {
        if i == left { push!("{colour}") }
        if i == right { push!("{NORMAL}") }
        push!("{c}");
    }
    push!("{NORMAL}\n");

    // Print source code underline.
    push!("{BLUE} {space:>w$} | {NORMAL}", w=digits);
    for _ in 0..left { push!(" "); }
    push!("{colour}");
    for _ in left..right { push!("^"); }
    push!("{NORMAL}");

    // Print the completed message.
    match level {
        LogLevel::Info  => log::info!( "{}", string),
        LogLevel::Warn  => log::warn!( "{}", string),
        LogLevel::Error => log::error!("{}", string),
        LogLevel::Fatal => log::fatal!("{}", string),
    }
}