125 lines
3.4 KiB
Rust
125 lines
3.4 KiB
Rust
use std::rc::Rc;
|
|
use thiserror::Error;
|
|
|
|
pub type Result<T> = core::result::Result<T, Error>;
|
|
|
|
#[derive(Error, Debug)]
|
|
pub enum ErrorKind {
|
|
#[error("error occurred during parse stage: {0}")]
|
|
ParseError(String),
|
|
#[error("error occurred during downgrade stage: {0}")]
|
|
DowngradeError(String),
|
|
#[error("error occurred during evaluation stage: {0}")]
|
|
EvalError(String),
|
|
#[error("{0}")]
|
|
Catchable(String),
|
|
#[error("an unknown or unexpected error occurred")]
|
|
Unknown,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Error {
|
|
pub kind: ErrorKind,
|
|
pub span: Option<rnix::TextRange>,
|
|
pub source: Option<Rc<str>>,
|
|
}
|
|
|
|
impl std::fmt::Display for Error {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
// Basic display
|
|
write!(f, "{}", self.kind)?;
|
|
|
|
// If we have source and span, print context
|
|
if let (Some(source), Some(span)) = (&self.source, self.span) {
|
|
let start_byte = usize::from(span.start());
|
|
let end_byte = usize::from(span.end());
|
|
|
|
if start_byte > source.len() || end_byte > source.len() {
|
|
return Ok(()); // Span is out of bounds
|
|
}
|
|
|
|
let mut start_line = 1;
|
|
let mut start_col = 1usize;
|
|
let mut line_start_byte = 0;
|
|
for (i, c) in source.char_indices() {
|
|
if i >= start_byte {
|
|
break;
|
|
}
|
|
if c == '\n' {
|
|
start_line += 1;
|
|
start_col = 1;
|
|
line_start_byte = i + 1;
|
|
} else {
|
|
start_col += 1;
|
|
}
|
|
}
|
|
|
|
let line_end_byte = source[line_start_byte..]
|
|
.find('\n')
|
|
.map(|i| line_start_byte + i)
|
|
.unwrap_or(source.len());
|
|
|
|
let line_str = &source[line_start_byte..line_end_byte];
|
|
|
|
let underline_len = if end_byte > start_byte {
|
|
end_byte - start_byte
|
|
} else {
|
|
1
|
|
};
|
|
|
|
write!(f, "\n --> {}:{}", start_line, start_col)?;
|
|
write!(f, "\n |\n")?;
|
|
writeln!(f, "{:4} | {}", start_line, line_str)?;
|
|
write!(
|
|
f,
|
|
" | {}{}",
|
|
" ".repeat(start_col.saturating_sub(1)),
|
|
"^".repeat(underline_len)
|
|
)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for Error {
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
Some(&self.kind)
|
|
}
|
|
}
|
|
|
|
impl Error {
|
|
pub fn new(kind: ErrorKind) -> Self {
|
|
Self {
|
|
kind,
|
|
span: None,
|
|
source: None,
|
|
}
|
|
}
|
|
|
|
pub fn with_span(mut self, span: rnix::TextRange) -> Self {
|
|
self.span = Some(span);
|
|
self
|
|
}
|
|
|
|
pub fn with_source(mut self, source: Rc<str>) -> Self {
|
|
self.source = Some(source);
|
|
self
|
|
}
|
|
|
|
pub fn parse_error(msg: String) -> Self {
|
|
Self::new(ErrorKind::ParseError(msg))
|
|
}
|
|
pub fn downgrade_error(msg: String) -> Self {
|
|
Self::new(ErrorKind::DowngradeError(msg))
|
|
}
|
|
pub fn eval_error(msg: String) -> Self {
|
|
Self::new(ErrorKind::EvalError(msg))
|
|
}
|
|
pub fn catchable(msg: String) -> Self {
|
|
Self::new(ErrorKind::Catchable(msg))
|
|
}
|
|
pub fn unknown() -> Self {
|
|
Self::new(ErrorKind::Unknown)
|
|
}
|
|
}
|