pochoir_lang/functions/
mod.rs

1//! Functions are used to transform data used in expressions.
2//!
3//! Some functions are built in (see the list of modules) but you can write your own functions and
4//! register them by inserting them in the context using [`Context::insert`].
5//!
6//! Functions must follow a certain signature:
7//! - References does not exist in the custom language so all arguments and the return type must be owned
8//! - The return type must be a [`FunctionResult`] with an inner value
9//!
10//! # Example
11//!
12//! To define the [`split`] function:
13//!
14//! ```
15//! use pochoir_lang::{FunctionResult, Value, IntoValue};
16//!
17//! pub(crate) fn split(val: String, separator: String) -> FunctionResult<Vec<String>> {
18//!     Ok(val.split(&separator).map(|v| v.to_string()).collect())
19//! }
20//! ```
21//!
22//! See the [crate documentation](`crate#functions-and-rust-integration`).
23//!
24//! [`Context::insert`]: crate::Context::insert
25#![allow(clippy::needless_pass_by_value)]
26#![allow(clippy::unnecessary_wraps)]
27
28use std::{error::Error, fmt, sync::Arc};
29
30use crate::{FromValue, IntoValue, Value};
31
32pub mod capitalize;
33pub mod ends_with;
34pub mod entries;
35pub mod enumerate;
36pub mod first;
37pub mod iter;
38pub mod last;
39pub mod len;
40pub mod reading_time;
41pub mod replace;
42pub mod reverse;
43pub mod round;
44pub mod skip;
45pub mod slugify;
46pub mod sort;
47pub mod split;
48pub mod starts_with;
49pub mod take;
50pub mod to_string;
51pub mod trim;
52pub mod word_count;
53
54pub type FunctionError = Box<dyn Error>;
55pub type FunctionResult<T> = Result<T, FunctionError>;
56
57#[derive(Clone)]
58pub struct Function(Arc<dyn Fn(Vec<Value>) -> FunctionResult<Value> + Send + Sync>);
59
60impl Function {
61    pub fn new<
62        Args: AsArguments + 'static,
63        Return: IntoValue,
64        F: Callable<Args, Return> + Send + Sync + 'static,
65    >(
66        func: F,
67    ) -> Self {
68        // We use a wrapper function calling the inner function to erase type parameters like
69        // the arguments and the return type
70        Self::new_raw(move |args: Vec<Value>| {
71            func.call(Args::as_arguments(args)?)
72                .map(IntoValue::into_value)
73        })
74    }
75
76    /// Make a new [`Function`] taking a list of arguments.
77    ///
78    /// In practice, it is not a commonly used function because [`Function::new`]
79    /// helps making a function with arguments automatically deserialized, so you should use
80    /// [`Function::new_raw`] only if you really need to get the list of un-deserialized arguments
81    /// as a single parameter.
82    pub fn new_raw<F: Fn(Vec<Value>) -> FunctionResult<Value> + Send + Sync + 'static>(f: F) -> Self {
83        Self(Arc::new(f))
84    }
85
86    /// Call the inner function.
87    ///
88    /// # Errors
89    ///
90    /// Errors are function-dependent and are returned as boxed [`std::error::Error`].
91    pub fn call(&self, args: Vec<Value>) -> Result<Value, Box<dyn Error>> {
92        (self.0)(args)
93    }
94}
95
96impl fmt::Debug for Function {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        f.debug_tuple("Function").finish()
99    }
100}
101
102/// A trait implemented for all function with up to 16 arguments providing an interface to call
103/// them using tuples.
104pub trait Callable<Args, Return> {
105    /// Call the inner function returning either a value or an error.
106    ///
107    /// # Errors
108    ///
109    /// Errors are function-dependent and are returned as boxed [`std::error::Error`].
110    fn call(&self, args: Args) -> Result<Return, Box<dyn Error>>;
111}
112
113/// A trait implemented for all tuples with up to 16 arguments providing a way to use them as
114/// arguments.
115pub trait AsArguments {
116    /// Transform a `&[Value]` into `Self`.
117    ///
118    /// # Errors
119    ///
120    /// Errors are function-dependent and are returned as boxed [`std::error::Error`].
121    fn as_arguments(val: Vec<Value>) -> Result<Self, Box<dyn Error>>
122    where
123        Self: Sized;
124}
125
126macro_rules! gen_impl {
127    ( $num_args:literal, $(($n:tt, $T:ident)),+ ) => {
128        impl<Func, Return, $($T: FromValue,)+> Callable<($($T,)+), Return> for Func
129        where
130            Func: Fn($($T,)+) -> Result<Return, Box<dyn Error>>,
131        {
132            fn call(&self, args: ($($T,)+)) -> Result<Return, Box<dyn Error>> {
133                (self)($(args.$n,)+)
134            }
135        }
136
137        impl<$($T: FromValue,)+> AsArguments for ($($T,)+) {
138            fn as_arguments(mut val: Vec<Value>) -> Result<Self, Box<dyn Error>> {
139                if val.len() > $num_args {
140                    Err(format!("expected {} argument{}, found {}", $num_args, if $num_args > 1 { "s" } else { "" }, val.len()).into())
141                } else if val.len() < $num_args {
142                    Ok(($(if $n < val.len() { $T::from_value(val.remove(0))? } else { $T::from_value(Value::Null)? },)+))
143                } else {
144                    Ok(($($T::from_value(val.remove(0))?,)+))
145                }
146            }
147        }
148    }
149}
150
151#[rustfmt::skip]
152mod impls {
153    use super::{AsArguments, Callable, Error, FromValue, Value};
154
155    impl<Func, Return> Callable<(), Return> for Func
156    where
157        Func: Fn() -> Result<Return, Box<dyn Error>>,
158    {
159        fn call(&self, _args: ()) -> Result<Return, Box<dyn Error>> {
160            (self)()
161        }
162    }
163
164    impl AsArguments for () {
165        fn as_arguments(val: Vec<Value>) -> Result<Self, Box<dyn Error>> {
166            if val.is_empty() {
167                Ok(())
168            } else {
169                Err(format!("expected 0 arguments, found {}", val.len()).into())
170            }
171        }
172    }
173
174    gen_impl!(1, (0, A));
175    gen_impl!(2, (0, A), (1, B));
176    gen_impl!(3, (0, A), (1, B), (2, C));
177    gen_impl!(4, (0, A), (1, B), (2, C), (3, D));
178    gen_impl!(5, (0, A), (1, B), (2, C), (3, D), (4, E));
179    gen_impl!(6, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F));
180    gen_impl!(7, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G));
181    gen_impl!(8, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H));
182    gen_impl!(9, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I));
183    gen_impl!(10, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J));
184    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));
185    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));
186    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));
187    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));
188    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));
189    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));
190}