#![allow(clippy::needless_pass_by_value)]
#![allow(clippy::unnecessary_wraps)]
use std::{error::Error, fmt, sync::Arc};
use crate::{FromValue, IntoValue, Value};
pub mod capitalize;
pub mod ends_with;
pub mod entries;
pub mod enumerate;
pub mod first;
pub mod iter;
pub mod last;
pub mod len;
pub mod reading_time;
pub mod replace;
pub mod reverse;
pub mod round;
pub mod skip;
pub mod slugify;
pub mod sort;
pub mod split;
pub mod starts_with;
pub mod take;
pub mod to_string;
pub mod trim;
pub mod word_count;
pub type FunctionError = Box<dyn Error>;
pub type FunctionResult<T> = Result<T, FunctionError>;
#[derive(Clone)]
pub struct Function(Arc<dyn Fn(Vec<Value>) -> FunctionResult<Value> + Send + Sync>);
impl Function {
pub fn new<
Args: AsArguments + 'static,
Return: IntoValue,
F: Callable<Args, Return> + Send + Sync + 'static,
>(
func: F,
) -> Self {
Self::new_raw(move |args: Vec<Value>| {
func.call(Args::as_arguments(args)?)
.map(IntoValue::into_value)
})
}
pub fn new_raw<F: Fn(Vec<Value>) -> FunctionResult<Value> + Send + Sync + 'static>(f: F) -> Self {
Self(Arc::new(f))
}
pub fn call(&self, args: Vec<Value>) -> Result<Value, Box<dyn Error>> {
(self.0)(args)
}
}
impl fmt::Debug for Function {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Function").finish()
}
}
pub trait Callable<Args, Return> {
fn call(&self, args: Args) -> Result<Return, Box<dyn Error>>;
}
pub trait AsArguments {
fn as_arguments(val: Vec<Value>) -> Result<Self, Box<dyn Error>>
where
Self: Sized;
}
macro_rules! gen_impl {
( $num_args:literal, $(($n:tt, $T:ident)),+ ) => {
impl<Func, Return, $($T: FromValue,)+> Callable<($($T,)+), Return> for Func
where
Func: Fn($($T,)+) -> Result<Return, Box<dyn Error>>,
{
fn call(&self, args: ($($T,)+)) -> Result<Return, Box<dyn Error>> {
(self)($(args.$n,)+)
}
}
impl<$($T: FromValue,)+> AsArguments for ($($T,)+) {
fn as_arguments(mut val: Vec<Value>) -> Result<Self, Box<dyn Error>> {
if val.len() > $num_args {
Err(format!("expected {} argument{}, found {}", $num_args, if $num_args > 1 { "s" } else { "" }, val.len()).into())
} else if val.len() < $num_args {
Ok(($(if $n < val.len() { $T::from_value(val.remove(0))? } else { $T::from_value(Value::Null)? },)+))
} else {
Ok(($($T::from_value(val.remove(0))?,)+))
}
}
}
}
}
#[rustfmt::skip]
mod impls {
use super::{AsArguments, Callable, Error, FromValue, Value};
impl<Func, Return> Callable<(), Return> for Func
where
Func: Fn() -> Result<Return, Box<dyn Error>>,
{
fn call(&self, _args: ()) -> Result<Return, Box<dyn Error>> {
(self)()
}
}
impl AsArguments for () {
fn as_arguments(val: Vec<Value>) -> Result<Self, Box<dyn Error>> {
if val.is_empty() {
Ok(())
} else {
Err(format!("expected 0 arguments, found {}", val.len()).into())
}
}
}
gen_impl!(1, (0, A));
gen_impl!(2, (0, A), (1, B));
gen_impl!(3, (0, A), (1, B), (2, C));
gen_impl!(4, (0, A), (1, B), (2, C), (3, D));
gen_impl!(5, (0, A), (1, B), (2, C), (3, D), (4, E));
gen_impl!(6, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F));
gen_impl!(7, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G));
gen_impl!(8, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H));
gen_impl!(9, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I));
gen_impl!(10, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J));
gen_impl!(11, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K));
gen_impl!(12, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K), (11, L));
gen_impl!(13, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K), (11, L), (12, M));
gen_impl!(14, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K), (11, L), (12, M), (13, N));
gen_impl!(15, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K), (11, L), (12, M), (13, N), (14, O));
gen_impl!(16, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K), (11, L), (12, M), (13, N), (14, O), (15, P));
}