use crate::{compile::TermId, filter::Vars, RcList};
use alloc::{boxed::Box, string::String, string::ToString, vec::Vec};
use core::fmt::{self, Display};
#[derive(Clone, Debug)]
pub struct Exn<'a, V>(pub(crate) Inner<'a, V>);
#[derive(Clone, Debug)]
pub(crate) enum Inner<'a, V> {
Err(Box<Error<V>>),
TailCall(Box<(&'a TermId, Vars<V>, CallInput<V>)>),
Break(usize),
}
#[derive(Clone, Debug)]
pub(crate) enum CallInput<V> {
Run(V),
Paths((V, RcList<V>)),
}
impl<V> CallInput<V> {
pub fn unwrap_run(self) -> V {
match self {
Self::Run(v) => v,
_ => panic!(),
}
}
pub fn unwrap_paths(self) -> (V, RcList<V>) {
match self {
Self::Paths(vp) => vp,
_ => panic!(),
}
}
}
impl<V> Exn<'_, V> {
pub(crate) fn get_err(self) -> Result<Error<V>, Self> {
match self.0 {
Inner::Err(e) => Ok(*e),
_ => Err(self),
}
}
}
impl<V> From<Error<V>> for Exn<'_, V> {
fn from(e: Error<V>) -> Self {
Exn(Inner::Err(Box::new(e)))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
enum Part<V, S = &'static str> {
Val(V),
Str(S),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Error<V>(Part<V, Vec<Part<V>>>);
impl<V> Error<V> {
pub fn new(v: V) -> Self {
Self(Part::Val(v))
}
pub fn path_expr(v: V) -> Self {
Self(Part::Str(Vec::from([
Part::Str("invalid path expression with input "),
Part::Val(v),
])))
}
pub fn typ(v: V, typ: &'static str) -> Self {
use Part::{Str, Val};
[Str("cannot use "), Val(v), Str(" as "), Str(typ)]
.into_iter()
.collect()
}
pub fn math(l: V, op: crate::ops::Math, r: V) -> Self {
use Part::{Str, Val};
[
Str("cannot calculate "),
Val(l),
Str(" "),
Str(op.as_str()),
Str(" "),
Val(r),
]
.into_iter()
.collect()
}
pub fn index(l: V, r: V) -> Self {
use Part::{Str, Val};
[Str("cannot index "), Val(l), Str(" with "), Val(r)]
.into_iter()
.collect()
}
}
impl<V: From<String>> Error<V> {
pub fn str(s: impl ToString) -> Self {
Self(Part::Val(V::from(s.to_string())))
}
}
impl<V> FromIterator<Part<V>> for Error<V> {
fn from_iter<T: IntoIterator<Item = Part<V>>>(iter: T) -> Self {
Self(Part::Str(iter.into_iter().collect()))
}
}
impl<V: From<String> + Display> Error<V> {
pub fn into_val(self) -> V {
if let Part::Val(v) = self.0 {
v
} else {
V::from(self.to_string())
}
}
}
impl<V: Display> Display for Error<V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.0 {
Part::Val(v) => v.fmt(f),
Part::Str(parts) => parts.iter().try_for_each(|part| match part {
Part::Val(v) => v.fmt(f),
Part::Str(s) => s.fmt(f),
}),
}
}
}