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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use super::CompileError;
use std::fmt::{self, Display};
pub use crate::colour_format;

pub const LINE_LIMIT: u8 = 24;

impl Display for CompileError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let ln = self.position.0.ln.to_string();
        let (line, arrw) = if self.position.0.ln == self.position.1.ln {
            sample(
                self.position.0.get_ln().unwrap(), // Position should be valid
                self.position.0.ln_idx,
                self.position.1.ln_idx,
                &self.msg,
                false,
            )
        } else {
            sample(
                self.position.0.get_ln().unwrap(), // Position should be valid
                self.position.0.ln_idx,
                self.position.0.get_ln().unwrap().len() as u16, // Position should be valid
                &self.msg,
                true,
            )
        };

        let out = colour_format![
            red("\nerror["), yellow(self.id), red("]: "), none(self.error_type),
            blue("\n --> "), cyan(&self.position.0.file_name),
            blue(":"), yellow(&ln), blue(":"), yellow(&self.position.0.ln_idx.to_string()),
            none("\n"), yellow(&ln), blue(" | "), none(&line),
            none("\n"), none(&" ".repeat(ln.len())), blue(" | ") red(&arrw),
            blue("\n <--"),
        ];

        write!(f, "{}", out)
    }
}

fn sample(line: &str, start_idx: u16, end_idx: u16, msg: &str, multi_line: bool) -> (String, String) {
    let start_trim = cal_trim(start_idx, 0);
    let end_trim = cal_trim(line.len() as u16, end_idx);

    let mut sample = line[start_trim as usize..line.len() - end_trim as usize].to_string();

    if start_trim != 0 { sample = colour_format![cyan("..."), none(&sample)]; }
    if end_trim != 0 { sample = colour_format![none(&sample), cyan("...")]; }

    if multi_line { sample = colour_format![none(&sample), cyan("\\n"), red("...")]; }

    (sample, gen_arrw(start_idx, end_idx, msg, start_trim, 5 * multi_line as u16))
}

#[inline]
fn cal_trim(actual: u16, desired: u16) -> u16 {
    let dif = actual.saturating_sub(desired);
    if dif > LINE_LIMIT as u16 { dif - LINE_LIMIT as u16 + 3 } // accounts for the `...`
    else { 0 }
}

#[inline]
fn gen_arrw(start_idx: u16, end_idx: u16, msg: &str, start_trim: u16, offset: u16) -> String {
    let spaces_since_start = if start_trim > 0 {
        start_idx - start_trim + 2 // acounts for the `...` and padding
    } else { start_idx -1 };

    let inbetween = end_idx - start_idx + 1 + offset; // even if it's the same character you still need a pointer

    let mut out = " ".repeat(spaces_since_start as usize);
    out.push_str(&"^".repeat(inbetween as usize));
    out.push(' ');
    out.push_str(&msg.replace('\n', "\x1b[36m\\n\x1b[31m"));
    
    out
}

/// Used to format text with colour
/// # Examples
/// ```rust
/// flexar::colour_format![pink("["), none("Logger"), pink("] "), none("Example Log")];
/// // outputs: [Logger] Example Log
/// // but with colour
/// ```
#[macro_export]
macro_rules! colour_format { // Verbose ugly stuff I can't read
    ($(
        $(none($none:expr))?
        $(blue($blue:expr))?
        $(pink($pink:expr))?
        $(white($white:expr))?
        $(green($green:expr))?
        $(cyan($cyan:expr))?
        $(red($red:expr))?
        $(black($black:expr))?
        $(yellow($yellow:expr))?
    ),*) => {{
        let mut string = String::new();
        $(
            $(string.push_str("\x1b[0m"); string.push_str($none);)?
            $(string.push_str("\x1b[34m"); string.push_str($blue);)?
            $(string.push_str("\x1b[35m"); string.push_str($pink);)?
            $(string.push_str("\x1b[37m"); string.push_str($white);)?
            $(string.push_str("\x1b[32m"); string.push_str($green);)?
            $(string.push_str("\x1b[36m"); string.push_str($cyan);)?
            $(string.push_str("\x1b[31m"); string.push_str($red);)?
            $(string.push_str("\x1b[30m"); string.push_str($black);)?
            $(string.push_str("\x1b[33m"); string.push_str($yellow);)?
        )* string.push_str("\x1b[0m");
        string
    }}
}