use crate::Error;
use crate::ast::Value;
use std::iter::FusedIterator;
use std::marker::PhantomData;
use std::sync::Arc;
pub(crate) type OperationFn = dyn Fn(Vec<Value>) -> Result<Value, Error> + Send + Sync;
pub(crate) trait FromParam {
type Param<'a>;
fn from_arg<'a>(value: &'a mut Value) -> Result<Self::Param<'a>, Error>;
}
impl FromParam for Value {
type Param<'a> = Value;
fn from_arg<'a>(value: &'a mut Value) -> Result<Self::Param<'a>, Error> {
Ok(std::mem::replace(value, Value::Unspecified))
}
}
impl<T> FromParam for T
where
Value: std::convert::TryInto<T, Error = Error>,
{
type Param<'a> = T;
fn from_arg<'a>(value: &'a mut Value) -> Result<Self::Param<'a>, Error> {
let owned = std::mem::replace(value, Value::Unspecified);
<Value as std::convert::TryInto<T>>::try_into(owned)
}
}
impl FromParam for &str {
type Param<'a> = &'a str;
fn from_arg<'a>(value: &'a mut Value) -> Result<Self::Param<'a>, Error> {
if let Value::String(s) = value {
Ok(s.as_str())
} else {
Err(Error::TypeError("expected string".into()))
}
}
}
impl<'b, K> FromParam for TypedValueIter<'b, K>
where
K: ValueElementKind,
{
type Param<'a> = TypedValueIter<'a, K>;
fn from_arg<'a>(value: &'a mut Value) -> Result<Self::Param<'a>, Error> {
if let Value::List(items) = value {
TypedValueIter::<K>::new(items.as_slice())
} else {
Err(Error::TypeError("expected list".into()))
}
}
}
#[doc(hidden)]
pub trait ValueElementKind {
type Item<'a>;
fn precheck(slice: &[Value]) -> Result<(), Error>;
fn project<'a>(v: &'a Value) -> Self::Item<'a>;
}
#[doc(hidden)]
pub struct TypedValueIter<'a, K: ValueElementKind> {
inner: std::slice::Iter<'a, Value>,
_marker: PhantomData<K>,
}
impl<'a, K> TypedValueIter<'a, K>
where
K: ValueElementKind,
{
pub(crate) fn new(values: &'a [Value]) -> Result<Self, Error> {
K::precheck(values)?;
Ok(TypedValueIter {
inner: values.iter(),
_marker: PhantomData,
})
}
}
impl<'a, K> Iterator for TypedValueIter<'a, K>
where
K: ValueElementKind,
{
type Item = K::Item<'a>;
fn next(&mut self) -> Option<Self::Item> {
let v = self.inner.next()?;
Some(K::project(v))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<'a, K> ExactSizeIterator for TypedValueIter<'a, K> where K: ValueElementKind {}
impl<'a, K> FusedIterator for TypedValueIter<'a, K> where K: ValueElementKind {}
#[doc(hidden)]
pub struct ValueKind;
impl ValueElementKind for ValueKind {
type Item<'a> = &'a Value;
fn precheck(_slice: &[Value]) -> Result<(), Error> {
Ok(())
}
fn project<'a>(v: &'a Value) -> Self::Item<'a> {
v
}
}
#[doc(hidden)]
pub struct NumberKind;
impl ValueElementKind for NumberKind {
type Item<'a> = i64;
fn precheck(slice: &[Value]) -> Result<(), Error> {
for v in slice {
if !matches!(v, Value::Number(_)) {
return Err(Error::TypeError("expected number".into()));
}
}
Ok(())
}
fn project<'a>(v: &'a Value) -> Self::Item<'a> {
if let Value::Number(n) = v {
*n
} else {
debug_assert!(false, "NumberKind::project saw non-number after precheck");
unreachable!("NumberKind invariant violated")
}
}
}
#[doc(hidden)]
pub struct BoolKind;
impl ValueElementKind for BoolKind {
type Item<'a> = bool;
fn precheck(slice: &[Value]) -> Result<(), Error> {
for v in slice {
if !matches!(v, Value::Bool(_)) {
return Err(Error::TypeError("expected boolean".into()));
}
}
Ok(())
}
fn project<'a>(v: &'a Value) -> Self::Item<'a> {
if let Value::Bool(b) = v {
*b
} else {
debug_assert!(false, "BoolKind::project saw non-boolean after precheck");
unreachable!("BoolKind invariant violated")
}
}
}
#[doc(hidden)]
pub struct StringKind;
impl ValueElementKind for StringKind {
type Item<'a> = &'a str;
fn precheck(slice: &[Value]) -> Result<(), Error> {
for v in slice {
if !matches!(v, Value::String(_)) {
return Err(Error::TypeError("expected string".into()));
}
}
Ok(())
}
fn project<'a>(v: &'a Value) -> Self::Item<'a> {
if let Value::String(s) = v {
s.as_str()
} else {
debug_assert!(false, "StringKind::project saw non-string after precheck");
unreachable!("StringKind invariant violated")
}
}
}
pub type ValueIter<'a> = TypedValueIter<'a, ValueKind>;
pub type NumIter<'a> = TypedValueIter<'a, NumberKind>;
pub type BoolIter<'a> = TypedValueIter<'a, BoolKind>;
pub type StringIter<'a> = TypedValueIter<'a, StringKind>;
pub(crate) trait FromRest {
type Param<'a>;
fn from_rest<'a>(slice: &'a [Value]) -> Result<Self::Param<'a>, Error>;
}
impl<K> FromRest for TypedValueIter<'static, K>
where
K: ValueElementKind,
{
type Param<'a> = TypedValueIter<'a, K>;
fn from_rest<'a>(slice: &'a [Value]) -> Result<Self::Param<'a>, Error> {
TypedValueIter::<K>::new(slice)
}
}
pub(crate) trait IntoValueResult {
fn into_value_result(self) -> Result<Value, Error>;
}
impl<T> IntoValueResult for Result<T, Error>
where
T: Into<Value>,
{
fn into_value_result(self) -> Result<Value, Error> {
self.map(Into::into)
}
}
impl<T> IntoValueResult for T
where
T: Into<Value>,
{
fn into_value_result(self) -> Result<Value, Error> {
Ok(self.into())
}
}
pub(crate) trait IntoOperation<Args> {
fn into_operation(self) -> Arc<OperationFn>;
}
pub(crate) trait IntoVariadicOperation<Args> {
fn into_variadic_operation(self) -> Arc<OperationFn>;
}
impl<F, I, R> IntoVariadicOperation<(I,)> for F
where
I: FromRest,
F: for<'a> Fn(<I as FromRest>::Param<'a>) -> R + Send + Sync + 'static,
R: IntoValueResult,
{
fn into_variadic_operation(self) -> Arc<OperationFn> {
Arc::new(move |args: Vec<Value>| {
let rest_param: <I as FromRest>::Param<'_> = <I as FromRest>::from_rest(&args[..])?;
let result: R = (self)(rest_param);
result.into_value_result()
})
}
}
macro_rules! impl_into_variadic_operation_for_prefix_and_rest {
($prefix:expr, $( $v:ident, $p:ident : $A:ident ),+ ) => {
impl<F, I, R, $( $A ),+> IntoVariadicOperation<( $( $A, )+ I, )> for F
where
I: FromRest,
$( $A: FromParam, )+
F: for<'a> Fn(
$( <$A as FromParam>::Param<'a> ),+,
<I as FromRest>::Param<'a>,
) -> R
+ Send
+ Sync
+ 'static,
R: IntoValueResult,
{
fn into_variadic_operation(self) -> Arc<OperationFn> {
Arc::new(move |mut args: Vec<Value>| {
let len = args.len();
match args.as_mut_slice() {
&mut [ $( ref mut $v ),+, ref mut rest @ .. ] => {
$(
let $p: <$A as FromParam>::Param<'_> =
<$A as FromParam>::from_arg($v)?;
)+
let rest_param: <I as FromRest>::Param<'_> =
<I as FromRest>::from_rest(&*rest)?;
let result: R = (self)( $( $p ),+, rest_param );
result.into_value_result()
}
_ => Err(Error::arity_error($prefix, len)),
}
})
}
}
};
}
impl_into_variadic_operation_for_prefix_and_rest!(1, v0, p0: A1);
impl_into_variadic_operation_for_prefix_and_rest!(2, v0, p0: A1, v1, p1: A2);
impl_into_variadic_operation_for_prefix_and_rest!(3, v0, p0: A1, v1, p1: A2, v2, p2: A3);
impl_into_variadic_operation_for_prefix_and_rest!(4, v0, p0: A1, v1, p1: A2, v2, p2: A3, v3, p3: A4);
impl_into_variadic_operation_for_prefix_and_rest!(5, v0, p0: A1, v1, p1: A2, v2, p2: A3, v3, p3: A4, v4, p4: A5);
impl_into_variadic_operation_for_prefix_and_rest!(6, v0, p0: A1, v1, p1: A2, v2, p2: A3, v3, p3: A4, v4, p4: A5, v5, p5: A6);
impl_into_variadic_operation_for_prefix_and_rest!(7, v0, p0: A1, v1, p1: A2, v2, p2: A3, v3, p3: A4, v4, p4: A5, v5, p5: A6, v6, p6: A7);
macro_rules! impl_into_operation_for_arity {
($arity:expr, $( $v:ident, $p:ident : $A:ident ),+ ) => {
impl<F, R, $( $A ),+> IntoOperation<( $( $A, )+ )> for F
where
F: for<'a> Fn( $( <$A as FromParam>::Param<'a> ),+ ) -> R
+ Send
+ Sync
+ 'static,
$( $A: FromParam, )+
R: IntoValueResult,
{
fn into_operation(self) -> Arc<OperationFn> {
Arc::new(move |mut args: Vec<Value>| {
let len = args.len();
match args.as_mut_slice() {
&mut [ $( ref mut $v ),+ ] => {
$(
let $p: <$A as FromParam>::Param<'_> =
<$A as FromParam>::from_arg($v)?;
)+
let result: R = (self)( $( $p ),+ );
result.into_value_result()
}
_ => Err(Error::arity_error($arity, len)),
}
})
}
}
};
}
impl<F, R> IntoOperation<()> for F
where
F: Fn() -> R + Send + Sync + 'static,
R: IntoValueResult,
{
fn into_operation(self) -> Arc<OperationFn> {
Arc::new(move |args: Vec<Value>| {
if !args.is_empty() {
return Err(Error::arity_error(0, args.len()));
}
let result: R = (self)();
result.into_value_result()
})
}
}
impl_into_operation_for_arity!(1, v0, p0: A1);
impl_into_operation_for_arity!(2, v0, p0: A1, v1, p1: A2);
impl_into_operation_for_arity!(3, v0, p0: A1, v1, p1: A2, v2, p2: A3);
impl_into_operation_for_arity!(4, v0, p0: A1, v1, p1: A2, v2, p2: A3, v3, p3: A4);
impl_into_operation_for_arity!(5, v0, p0: A1, v1, p1: A2, v2, p2: A3, v3, p3: A4, v4, p4: A5);
impl_into_operation_for_arity!(6, v0, p0: A1, v1, p1: A2, v2, p2: A3, v3, p3: A4, v4, p4: A5, v5, p5: A6);
impl_into_operation_for_arity!(7, v0, p0: A1, v1, p1: A2, v2, p2: A3, v3, p3: A4, v4, p4: A5, v5, p5: A6, v6, p6: A7);
impl_into_operation_for_arity!(8, v0, p0: A1, v1, p1: A2, v2, p2: A3, v3, p3: A4, v4, p4: A5, v5, p5: A6, v6, p6: A7, v7, p7: A8);