use std::error::Error;
use std::fmt;
use std::path::PathBuf;
use colored::*;
use crate::parser::utils::{next_new_line, previous_new_line, replicate};
use crate::parser::Position;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ErrorType {
UnmatchedStar,
UnmatchedSlash,
UnmatchedDollar,
MultipleLinesTitle,
}
impl ErrorType {
pub fn title(self) -> &'static str {
match self {
ErrorType::UnmatchedStar => "unmatched *",
ErrorType::UnmatchedSlash => "unmactched /",
ErrorType::UnmatchedDollar => "unmactched $",
ErrorType::MultipleLinesTitle => "titles must be followed by an empty line",
}
}
pub fn detail(self) -> &'static str {
match self {
ErrorType::UnmatchedStar => "bold content starts here but never ends",
ErrorType::UnmatchedSlash => "italic content starts here but never ends",
ErrorType::UnmatchedDollar => "inline inlinemath starts here but never ends",
ErrorType::MultipleLinesTitle => "expected empty line here",
}
}
pub fn note(self) -> Option<&'static str> {
match self {
ErrorType::UnmatchedStar => None,
ErrorType::UnmatchedSlash => None,
ErrorType::UnmatchedDollar => None,
ErrorType::MultipleLinesTitle => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EmptyError {
pub position: Position,
pub ty: ErrorType,
}
#[derive(Debug)]
pub struct Errors {
pub path: PathBuf,
pub content: String,
pub errors: Vec<EmptyError>,
}
impl fmt::Display for Errors {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
for error in &self.errors {
let start = previous_new_line(&self.content, error.position.offset);
let end = next_new_line(&self.content, error.position.offset);
let line = error.position.line;
let column = error.position.column;
let line_number = format!("{} ", line);
let space = replicate(' ', line_number.len() - 1);
let margin = replicate(' ', column);
let hats = replicate('^', 1);
writeln!(fmt, "{}{}", "error: ".bold().red(), error.ty.title().bold())?;
writeln!(
fmt,
"{}{} {}:{}:{}",
space,
"-->".bold().blue(),
self.path.display(),
line,
column
)?;
writeln!(fmt, "{} {}", space, "|".blue().bold())?;
writeln!(
fmt,
"{} {}",
&format!("{}|", line_number).blue().bold(),
&self.content[start..end]
)?;
writeln!(
fmt,
"{} {}{}{} {}",
space,
"|".blue().bold(),
margin,
hats.bold().red(),
error.ty.detail().bold().red()
)?;
writeln!(fmt, "{} {}", space, "|".blue().bold())?;
if let Some(note) = error.ty.note() {
writeln!(
fmt,
"{} {} {}{}",
space,
"=".blue().bold(),
"note: ".bold(),
note
)?;
}
}
Ok(())
}
}
impl Error for Errors {}