use std::{fmt::{Debug, Display}, error::Error, io}; use crate::prelude::*; #[macro_export] macro_rules! exception { ($type:expr) => { $crate::prelude::Exception::new($type) }; ($type:expr, $($arg:tt)*) => { $crate::prelude::Exception::msg($type, format!($($arg)*).as_str()) }; } pub const HASH_EXCEPTION: &'static str = "hash"; pub const VALUE_EXCEPTION: &'static str = "value"; pub const PARSE_EXCEPTION: &'static str = "parse"; pub const COMPILE_EXCEPTION: &'static str = "compile"; pub const RUNTIME_EXCEPTION: &'static str = "runtime"; pub const BINARY_EXECPTION: &'static str = "binary"; pub const IO_EXECPTION: &'static str = "io"; #[derive(Clone)] pub struct Exception(Gc); #[derive(Clone)] struct ExceptionInner { ty: Rc, msg: Rc, trace: Vec<(Rc, Position)> } impl Exception { pub fn msg(ty: &str, msg: &str) -> Self { Self(Gc::new(ExceptionInner { ty: ty.into(), msg: msg.into(), trace: Vec::new() })) } pub fn pos(mut self, pos: Position) -> Self { self.0.trace.push(( Rc::from(""), pos )); self } pub fn trace(mut self, block: Rc, pos: Position) -> Self { self.0.trace.push(( block, pos )); self } } impl Display for Exception { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let ty = self.0.ty.as_ref(); let msg = self.0.msg.as_ref(); write!(f, "{}\n Type <{}>", msg, ty)?; for (block, pos) in self.0.trace.iter() { write!(f, "\n In {block} at {pos}\n")?; } Ok(()) } } impl Debug for Exception { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "[Exception {}]", self.0.ty.as_ref()) } } impl Error for Exception {} impl From for Result { fn from(value: Exception) -> Self { Err(value) } } impl From for Exception { fn from(value: io::Error) -> Self { exception!(IO_EXCEPTION, "{value}") } } pub trait Except { type Output; fn exception(self) -> Result; } impl Except for std::result::Result { type Output = T; fn exception(self) -> Result { self.map_err(|e| { Exception::msg("unknown", format!("{e}").as_str()) }) } }