whiledb 0.1.4

rust implementation of WhileDB language
Documentation
use colored::Colorize;
use santiago::lexer::LexerError;
use santiago::parser::ParseError;
use crate::ast::*;

#[derive(Debug)]
pub enum SrcError<Val> {
    LexerError(LexerError, String),
    ParseError(ParseError<Cmd>, String),
    SelfError(String),
    SelfWarning(Val, String)
}

impl<Val> std::fmt::Display for SrcError<Val> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
            SrcError::LexerError(_, msg) => msg,
            SrcError::ParseError(_, msg) => msg,
            SrcError::SelfError(msg) => msg,
            SrcError::SelfWarning(_, msg) => msg,
        })
    }
}

fn line_start(num: usize, num_width: usize) -> String {
    let num = num.to_string();
    return " ".repeat(num_width - num.len()) + &num + " ";
}

pub fn lexer_error_msg(src: &str, err: &LexerError) -> String {
    let mut res = "[LexerError]".bright_yellow().to_string() + ": ";
    res.push_str(&err.position.to_string());
    res.push('\n');
    let lines_src: Vec<&str> = src.lines().collect();

    let start = std::cmp::max(1, err.position.line as i32 - 5) as usize;
    let end = std::cmp::min(lines_src.len(), err.position.line + 5);
    let num_width = std::cmp::max(start.to_string().len(), end.to_string().len());
    
    for line_num in start..=end {
        if line_num == err.position.line {
            res.push_str(&line_start(line_num, num_width).on_bright_red().to_string());
            {
                let tmp: Vec<char> = lines_src[line_num - 1].chars().collect();
                for idx in 0..err.position.column - 1 {
                    res.push(tmp[idx]);
                }
                res.push_str(&tmp[err.position.column - 1].to_string().bright_red().underline().to_string());
                for idx in err.position.column..tmp.len() {
                    res.push(tmp[idx]);
                }
            }
        }
        else {
            res.push_str(&line_start(line_num, num_width).on_truecolor(0, 128, 0).to_string());
            res.push_str(lines_src[line_num - 1]);
        }
        res.push('\n');
    }
    res
}

pub fn parse_error_msg(src: &str, err: &ParseError<Cmd>) -> String {
    let mut res = "[ParseError]".bright_yellow().to_string() + ": ";
    if let Some(err_at) = &err.at {
        res.push_str(&err_at.to_string());
    }
    res.push('\n');

    match &err.at {
        Some(lexeme) => {
            let pos = &lexeme.as_ref().position;
            let lines_src: Vec<&str> = src.lines().collect();

            let start = std::cmp::max(1, pos.line as i32 - 5) as usize;
            let end = std::cmp::min(lines_src.len(), pos.line + 5);
            let num_width = std::cmp::max(start.to_string().len(), end.to_string().len());

            for line_num in start..=end {
                if line_num == pos.line {
                    res.push_str(&line_start(line_num, num_width).on_bright_red().to_string());
                    {
                        let token = &lexeme.as_ref().raw;
                        res.push_str(&lines_src[line_num - 1][..(pos.column - 1)]);
                        res.push_str(&token.bright_red().underline().to_string());
                        res.push_str(&lines_src[line_num - 1][(pos.column - 1 + token.len())..])
                    }
                }
                else {
                    res.push_str(&line_start(line_num, num_width).on_truecolor(0, 128, 0).to_string());
                    res.push_str(lines_src[line_num - 1]);
                }
                res.push('\n');
            }

            res
        },
        None => res,
    }
}