Lines
77.42 %
Functions
23.33 %
Branches
100 %
use std::fmt;
use std::io::IsTerminal;
use std::ops::Range;
use thiserror::Error;
#[derive(Debug, Clone)]
pub struct ParseErrorDetails {
pub message: String,
pub input: String,
pub span: Range<usize>,
}
impl ParseErrorDetails {
pub fn new(message: String, input: String, span: Range<usize>) -> Self {
Self {
message,
input,
span,
fn find_line_info(&self) -> (usize, usize, &str) {
let offset = self.span.start.min(self.input.len());
let before = &self.input[..offset];
let line_num = before.matches('\n').count() + 1;
let line_start = before.rfind('\n').map_or(0, |i| i + 1);
let line_end = self.input[offset..]
.find('\n')
.map_or(self.input.len(), |i| offset + i);
let line = &self.input[line_start..line_end];
let col = offset - line_start;
(line_num, col, line)
pub fn render(&self, use_color: bool) -> String {
let (line_num, col, line) = self.find_line_info();
let line_num_width = line_num.to_string().len().max(1);
let (red, cyan, reset) = if use_color {
("\x1b[31m", "\x1b[36m", "\x1b[0m")
} else {
("", "", "")
};
let mut output = String::new();
output.push_str(&format!("{red}error:{reset} {}\n", self.message));
output.push_str(&format!(
"{cyan}{:>width$} |{reset}\n",
"",
width = line_num_width
));
"{cyan}{line_num:>line_num_width$} |{reset} {line}\n"
"{cyan}{:>width$} |{reset} {}{red}^{reset}\n",
" ".repeat(col),
output
pub fn render_auto(&self) -> String {
self.render(std::io::stderr().is_terminal())
impl fmt::Display for ParseErrorDetails {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.render(false))
#[derive(Error, Debug)]
pub enum Error {
#[error("{0}")]
Parse(ParseErrorDetails),
#[error("Compile error: {0}")]
Compile(String),
#[error("Runtime error: {0}")]
Runtime(String),
#[error("Undefined symbol: {0}")]
UndefinedSymbol(String),
#[error("Type error: expected {expected}, got {actual}")]
Type { expected: String, actual: String },
#[error("Arity error: {name} expects {expected} arguments, got {actual}")]
Arity {
name: String,
expected: usize,
actual: usize,
},
impl Error {
#[must_use]
pub fn parse(message: String, input: &str, span: Range<usize>) -> Self {
Self::Parse(ParseErrorDetails::new(message, input.to_string(), span))
match self {
Error::Parse(details) => details.render(use_color),
_ => self.to_string(),
Error::Parse(details) => details.render_auto(),
pub type Result<T> = std::result::Result<T, Error>;