use crate::{TulispContext, TulispObject};
macro_rules! replace_expr {
($_t:ty, $sub:ident) => {
$sub
};
}
macro_rules! ErrorKind {
($(
($kind:ident$(($param:ty))? $(, $vis:vis $ctor:ident)?)
),* $(,)?) => {
#[derive(Debug, Clone)]
pub enum ErrorKind {
$(
$kind$(( $param ))?,
)*
}
impl std::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$(
Self::$kind$((replace_expr!($param, vv)))? => {
write!(
f,
"{}",
stringify!($kind)
$(.to_owned() + "(" + &replace_expr!($param, vv).to_string() + ")")?
)
},
)*
}
}
}
impl Error {
$(
$(
#[doc = concat!(
"Creates a new [`Error`] with the `",
stringify!($kind),
"` kind and the given description."
)]
$vis fn $ctor(desc: impl Into<String>) -> crate::error::Error {
Self {
kind: ErrorKind::$kind,
desc: desc.into(),
backtrace: vec![],
}
}
)?
)*
}
};
}
ErrorKind!(
(InvalidArgument, pub invalid_argument),
(LispError, pub lisp_error),
(NotImplemented, pub not_implemented),
(OutOfRange, pub out_of_range),
(OSError, pub os_error),
(TypeMismatch, pub type_mismatch),
(PlistError, pub plist_error),
(MissingArgument, pub missing_argument),
(Undefined, pub(crate) undefined),
(Uninitialized, pub(crate) uninitialized),
(ParsingError, pub(crate) parsing_error),
(SyntaxError, pub(crate) syntax_error),
(Throw(TulispObject)), );
#[derive(Debug, Clone)]
pub struct Error {
kind: ErrorKind,
desc: String,
backtrace: Vec<TulispObject>,
}
impl Error {
pub fn throw(tag: TulispObject, value: TulispObject) -> Self {
Self {
kind: ErrorKind::Throw(TulispObject::cons(tag, value)),
desc: String::new(),
backtrace: vec![],
}
}
fn format_span(&self, ctx: &TulispContext, object: &TulispObject) -> String {
if let Some(span) = object.span() {
let filename = ctx.get_filename(span.file_id);
format!(
"{}:{}.{}-{}.{}:",
filename, span.start.0, span.start.1, span.end.0, span.end.1
)
} else {
String::new()
}
}
pub fn format(&self, ctx: &TulispContext) -> String {
let mut span_str = format!(
"ERR {}:{}",
self.kind,
if self.desc.is_empty() {
String::new()
} else {
format!(" {}", self.desc)
}
);
for span in &self.backtrace {
let prefix = self.format_span(ctx, span);
if prefix.is_empty() {
continue;
}
if span.numberp() || span.symbolp() || span.stringp() {
continue;
}
let string = span.to_string().replace("\n", "\\n");
if string.len() > 80 {
span_str.push_str(&format!("\n{} at {:.80}...", prefix, string));
} else {
span_str.push_str(&format!("\n{} at {}", prefix, string));
}
}
span_str + "\n"
}
}
impl Error {
pub fn with_trace(mut self, span: TulispObject) -> Self {
if self.backtrace.last().is_some_and(|last| last.eq(&span)) {
return self;
}
self.backtrace.push(span);
self
}
pub fn kind(&self) -> ErrorKind {
self.kind.clone()
}
pub(crate) fn kind_ref(&self) -> &ErrorKind {
&self.kind
}
pub fn desc(&self) -> String {
self.desc.to_owned()
}
}