pochoir-lang 0.12.2

Custom parser and interpreter for the pochoir template engine
Documentation
//! Functions are used to transform data used in expressions.
//!
//! Some functions are built in (see the list of modules) but you can write your own functions and
//! register them by inserting them in the context using [`Context::insert`].
//!
//! Functions must follow a certain signature:
//! - References does not exist in the custom language so all arguments and the return type must be owned
//! - The return type must be a [`FunctionResult`] with an inner value
//!
//! # Example
//!
//! To define the [`split`] function:
//!
//! ```
//! use pochoir_lang::{FunctionResult, Value, IntoValue};
//!
//! pub(crate) fn split(val: String, separator: String) -> FunctionResult<Vec<String>> {
//!     Ok(val.split(&separator).map(|v| v.to_string()).collect())
//! }
//! ```
//!
//! See the [crate documentation](`crate#functions-and-rust-integration`).
//!
//! [`Context::insert`]: crate::Context::insert
#![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 {
        // We use a wrapper function calling the inner function to erase type parameters like
        // the arguments and the return type
        Self::new_raw(move |args: Vec<Value>| {
            func.call(Args::as_arguments(args)?)
                .map(IntoValue::into_value)
        })
    }

    /// Make a new [`Function`] taking a list of arguments.
    ///
    /// In practice, it is not a commonly used function because [`Function::new`]
    /// helps making a function with arguments automatically deserialized, so you should use
    /// [`Function::new_raw`] only if you really need to get the list of un-deserialized arguments
    /// as a single parameter.
    pub fn new_raw<F: Fn(Vec<Value>) -> FunctionResult<Value> + Send + Sync + 'static>(f: F) -> Self {
        Self(Arc::new(f))
    }

    /// Call the inner function.
    ///
    /// # Errors
    ///
    /// Errors are function-dependent and are returned as boxed [`std::error::Error`].
    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()
    }
}

/// A trait implemented for all function with up to 16 arguments providing an interface to call
/// them using tuples.
pub trait Callable<Args, Return> {
    /// Call the inner function returning either a value or an error.
    ///
    /// # Errors
    ///
    /// Errors are function-dependent and are returned as boxed [`std::error::Error`].
    fn call(&self, args: Args) -> Result<Return, Box<dyn Error>>;
}

/// A trait implemented for all tuples with up to 16 arguments providing a way to use them as
/// arguments.
pub trait AsArguments {
    /// Transform a `&[Value]` into `Self`.
    ///
    /// # Errors
    ///
    /// Errors are function-dependent and are returned as boxed [`std::error::Error`].
    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));
}