use super::*;
use std::fmt;
pub type EvalResult<T, E = Error> = std::result::Result<T, E>;
pub(super) trait EvalResultExt {
fn add_errors(self, rhs: Self) -> Self;
}
impl EvalResultExt for EvalResult<(), Errors> {
fn add_errors(self, rhs: Self) -> Self {
match self {
Err(mut lhs) => {
lhs.extend_from_result(rhs);
Err(lhs)
}
_ => rhs,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Errors {
inner: Vec<Error>,
}
impl Errors {
fn extend_from_result(&mut self, res: EvalResult<(), Errors>) {
if let Err(errors) = res {
self.inner.extend(errors);
}
}
#[inline]
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.inner.len()
}
#[inline]
pub fn iter(&self) -> std::slice::Iter<Error> {
self.inner.iter()
}
}
impl fmt::Display for Errors {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.len() == 1 {
self.inner[0].fmt(f)
} else {
writeln!(f, "{} errors occurred:", self.len())?;
for error in self {
writeln!(f, "- {error}")?;
}
Ok(())
}
}
}
impl From<Error> for Errors {
#[inline]
fn from(error: Error) -> Self {
Errors { inner: vec![error] }
}
}
impl std::error::Error for Errors {}
impl IntoIterator for Errors {
type Item = Error;
type IntoIter = std::vec::IntoIter<Error>;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl<'a> IntoIterator for &'a Errors {
type Item = &'a Error;
type IntoIter = std::slice::Iter<'a, Error>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Error {
inner: Box<ErrorInner>,
}
impl Error {
pub(super) fn new<T>(kind: T) -> Error
where
T: Into<ErrorKind>,
{
Error::new_with_expr(kind, None)
}
pub(super) fn new_with_expr<T>(kind: T, expr: Option<Expression>) -> Error
where
T: Into<ErrorKind>,
{
Error {
inner: Box::new(ErrorInner::new(kind.into(), expr)),
}
}
pub(super) fn unexpected<T>(value: T, expected: &'static str) -> Error
where
T: Into<Value>,
{
Error::new(ErrorKind::Unexpected(value.into(), expected))
}
pub fn kind(&self) -> &ErrorKind {
&self.inner.kind
}
pub fn expr(&self) -> Option<&Expression> {
self.inner.expr.as_ref()
}
pub fn into_kind(self) -> ErrorKind {
self.inner.kind
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.inner, f)
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Error::new(kind)
}
}
impl From<crate::Error> for Error {
fn from(err: crate::Error) -> Self {
Error::new(ErrorKind::Message(err.to_string()))
}
}
impl std::error::Error for Error {}
#[derive(Debug, Clone, PartialEq, Eq)]
struct ErrorInner {
kind: ErrorKind,
expr: Option<Expression>,
}
impl ErrorInner {
fn new(kind: ErrorKind, expr: Option<Expression>) -> ErrorInner {
ErrorInner { kind, expr }
}
}
impl fmt::Display for ErrorInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.kind)?;
if let Some(expr) = &self.expr {
write!(f, " in expression `{expr}`")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorKind {
Message(String),
UndefinedVar(Identifier),
UndefinedFunc(FuncName),
Unexpected(Value, &'static str),
Index(usize),
UnaryOp(UnaryOperator, Value),
BinaryOp(Value, BinaryOperator, Value),
NoSuchKey(String),
KeyExists(String),
FuncCall(FuncName, String),
}
impl From<Error> for ErrorKind {
fn from(err: Error) -> Self {
err.into_kind()
}
}
impl From<&str> for ErrorKind {
fn from(msg: &str) -> Self {
ErrorKind::Message(msg.to_owned())
}
}
impl From<String> for ErrorKind {
fn from(msg: String) -> Self {
ErrorKind::Message(msg)
}
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorKind::Message(msg) => f.write_str(msg),
ErrorKind::UndefinedVar(ident) => {
write!(f, "undefined variable `{ident}`")
}
ErrorKind::UndefinedFunc(func_name) => {
write!(f, "undefined function `{func_name}`")
}
ErrorKind::Unexpected(value, expected) => {
write!(f, "unexpected value `{value}`, expected {expected}")
}
ErrorKind::Index(index) => write!(f, "index out of bounds: {index}"),
ErrorKind::NoSuchKey(key) => write!(f, "no such key: `{key}`"),
ErrorKind::KeyExists(key) => write!(f, "key `{key}` already exists"),
ErrorKind::UnaryOp(operator, value) => write!(
f,
"unary operator `{operator}` is not applicable to `{value}`",
),
ErrorKind::BinaryOp(lhs, operator, rhs) => write!(
f,
"binary operator `{operator}` is not applicable to `{lhs}` and `{rhs}`",
),
ErrorKind::FuncCall(name, msg) => {
write!(f, "error calling function `{name}`: {msg}")
}
}
}
}