use std::num::TryFromIntError;
use std::ops::Range;
use itertools::Itertools;
use thiserror::Error;
use crate::model::*;
#[derive(Debug, Error, PartialEq)]
pub enum Error {
#[error("unknown identifier: {ident}")]
Name {
ident: Ident,
},
#[error(
"type mismatch: expected {expected}; got ({actual})",
expected = expected.iter().format_with(" or ", |sig, f| {
f(&format_args!("({})", sig.iter().format(", ")))
}),
actual = actual.iter().format(", "),
)]
Type {
expected: Vec<Vec<Type>>,
actual: Vec<Type>,
},
#[error("invalid argument {actual:?}, expected {expected}")]
Argument {
expected: String, actual: Value,
},
#[error("index out of bounds: {actual} is not in {expected:?} range")]
Bounds {
expected: Range<usize>,
actual: usize,
},
#[error("conversion error: {0}")]
Conversion(ConversionError),
#[error("custom error: {0}")]
Custom(String),
}
impl Error {
pub fn name(ident: impl Into<Ident>) -> Self {
Self::Name { ident: ident.into() }
}
pub fn argument(expected: impl Into<String>, actual: impl Into<Value>) -> Self {
Self::Argument { expected: expected.into(), actual: actual.into() }
}
pub fn bounds(expected: Range<usize>, actual: usize) -> Self {
Self::Bounds { expected, actual }
}
pub fn custom(msg: impl Into<String>) -> Self {
Self::Custom(msg.into())
}
pub fn type_<E, S, A>(expected: E, actual: A) -> Self
where E: IntoIterator<Item=S>, S: IntoIterator<Item=Type>,
A: IntoIterator<Item=Type>
{
Self::Type {
expected: expected.into_iter().map(|sig| sig.into_iter().collect()).collect(),
actual: actual.into_iter().collect(),
}
}
}
impl<E> From<E> for Error
where ConversionError: From<E>
{
fn from(err: E) -> Self {
Self::Conversion(err.into())
}
}
#[derive(Debug, Error, Eq, PartialEq)]
pub enum ConversionError {
#[error("conversion from integer failed: {0}")]
FromInt(#[from] TryFromIntError),
}