use crate::token::Token;
use std::fmt;
#[derive(Clone, Debug, PartialEq)]
pub struct Error {
msg: String,
}
impl Error {
pub fn new(msg: String) -> Self {
Self { msg }
}
fn indexed_error(input: String, point: i32, err: String, expl: Vec<&str>) -> Self {
let mut message = err;
let tab = " ";
let space_count = if point > 1 { (point - 1) as usize } else { 0 };
let space: String = " ".repeat(space_count);
message.push_str(&format!("{tab}\"{}\" \n", input.trim_end()));
for exp in expl.iter() {
message.push_str(&format!(" {tab}{space}{exp}\n"));
}
Self { msg: message }
}
pub fn empty_input() -> Self {
Self {
msg: String::from("error: cannot parse an empty input"),
}
}
pub fn empty_tokens() -> Self {
Self {
msg: String::from("error: cannot calculate result from an empty token list"),
}
}
pub fn missing_some_tokens(input: String, point: i32) -> Self {
let message = "error: missing some tokens to calculate result\n\n".to_string();
let mut inpt: String = input.trim_end().to_string();
let pointer = " {X} ";
for (i, pch) in pointer.chars().enumerate() {
let p: i32 = point + (i as i32) + 1;
let backid: usize = if p < 1 { 0 } else { (p - 1) as usize };
let back_ch = inpt.chars().nth(backid).unwrap_or('0');
let next_ch = inpt.chars().nth((p + 1) as usize).unwrap_or('0');
if (back_ch == ' ' || next_ch == ' ') && pch == ' ' {
continue;
}
inpt.insert(p as usize, pch);
}
let explanation: Vec<&str> = Vec::from([
"|",
"| > Expected a token character.",
"| > hint: `42`, `+`, `-`, `/`, `*`, `%`, `^`.",
]);
Error::indexed_error(inpt, point + 4, message, explanation)
}
pub fn cannot_parse_to_number(input: String, token: Token) -> Self {
let message = format!(
"error: cannot parse token literal: `{}` to a number\n\n",
token.literal
);
let explanation: Vec<&str> = Vec::from([
"|",
"| > Cannot convert the character (that represented",
"| > as number) to the actual number representation.",
]);
Error::indexed_error(input, token.index.1 + 1, message, explanation)
}
pub fn invalid_order() -> Self {
let space = " ";
let mut msg = String::from("error: invalid order of token characters\n");
msg.push_str(&format!("{space}A valid token/character order is:"));
msg.push_str(&format!("{space}[Numerable], [Operation], [Numerable]"));
Self { msg }
}
pub fn illegal_token(input: String, token: Token) -> Self {
let message = format!(
"error: found an illegal character: `{}` \n\n",
token.literal
);
let explanation: Vec<&str> = Vec::from([
"|",
"| > We do not know how to parse this character",
"| > If you think this is a bug or a practical feature",
"| > that we do not have yet, please open an issue:",
"| > -> https://github.com/theiskaa/mate/issues/new",
]);
Error::indexed_error(input, token.index.1 + 1, message, explanation)
}
pub fn division_by_zero(input: String, point: i32) -> Self {
let message = String::from("error: division by zero\n\n");
let explanation: Vec<&str> = Vec::from([
"|",
"| > Cannot divide by zero.",
"| > hint: ensure the divisor is not zero.",
]);
Error::indexed_error(input, point, message, explanation)
}
pub fn mismatched_parentheses(input: String, point: i32) -> Self {
let message = String::from("error: mismatched parentheses or brackets\n\n");
let explanation: Vec<&str> = Vec::from([
"|",
"| > Found a closing bracket without a matching opening bracket,",
"| > or brackets are mismatched (e.g., '(' closed with ']').",
"| > hint: ensure all brackets are properly paired.",
]);
Error::indexed_error(input, point, message, explanation)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.msg)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new() {
let result: Error = Error::new(String::from("test message"));
assert_eq!(result.msg, String::from("test message"));
}
#[test]
fn empty_input() {
let result: Error = Error::empty_input();
assert_eq!(
result.msg,
String::from("error: cannot parse an empty input")
);
}
#[test]
fn empty_tokens() {
let result: Error = Error::empty_tokens();
assert_eq!(
result.msg,
String::from("error: cannot calculate result from an empty token list")
);
}
#[test]
fn display() {
let error: Error = Error::new(String::from("A new message"));
assert_eq!(format!("{}", error), error.msg)
}
}