use std::fs::File;
use std::io::*;
use crate::compiler::{Metadata, Token};
#[derive(Debug)]
pub enum ErrorPosition {
Pos(usize, usize),
EOF
}
#[derive(Debug)]
pub struct ErrorDetails {
pub position: ErrorPosition,
pub len: usize,
pub data: Option<String>,
}
impl ErrorDetails {
pub fn new(position: ErrorPosition, len: usize) -> Self {
ErrorDetails {
position,
len,
data: None
}
}
pub fn with_eof() -> Self {
ErrorDetails {
position: ErrorPosition::EOF,
len: 0,
data: None
}
}
pub fn with_pos((row, col): (usize, usize), len: usize) -> Self {
ErrorDetails {
position: ErrorPosition::Pos(row, col),
len,
data: None
}
}
pub fn from_metadata(meta: &impl Metadata) -> Self {
Self::from_token_option(meta.get_current_token())
}
pub fn from_token_option(token_opt: Option<Token>) -> Self {
match token_opt {
Some(token) => ErrorDetails::with_pos(token.pos, token.word.len()),
None => ErrorDetails::with_eof()
}
}
pub fn data<T: AsRef<str>>(mut self, data: T) -> Self {
self.data = Some(data.as_ref().to_string().clone());
self
}
pub fn get_pos_by_file_or_code(&self, path: Option<String>, code: Option<String>) -> (usize, usize) {
match self.position {
ErrorPosition::Pos(row, col) => (row, col),
ErrorPosition::EOF => {
if let Some(code) = code {
self.get_pos_by_code(code)
}
else if let Some(path) = path {
match self.get_pos_by_file(path) {
Ok((row, col)) => (row, col),
Err(_) => (0, 0)
}
}
else { (0, 0) }
}
}
}
pub fn get_pos_by_file(&self, path: impl AsRef<str>) -> std::io::Result<(usize, usize)> {
let mut code = format!("");
let mut file = File::open(path.as_ref())?;
file.read_to_string(&mut code)?;
Ok(self.get_pos_by_code(&code))
}
pub fn get_pos_by_code(&self, code: impl AsRef<str>) -> (usize, usize) {
let code = code.as_ref();
match self.position {
ErrorPosition::Pos(row, col) => (row, col),
ErrorPosition::EOF => {
let mut col = 1;
let mut row = 1;
col += code.split_whitespace().count();
if let Some(last) = code.split_whitespace().last() {
row += last.len();
}
return (row, col);
}
}
}
}