Skip to main content

filt_rs/functions/
mod.rs

1//! Built-in functions, and the [`Function`] trait for defining your own.
2//!
3//! Filters may call functions using the `name(args...)` syntax. The crate ships
4//! a small base set (see [`base_functions`]) — currently [`Trim`] and, with the
5//! `chrono` feature, [`Now`], [`Ago`], and [`DateTimeFn`] — but you can add your
6//! own by implementing
7//! [`Function`] and passing instances to
8//! [`Filter::with_functions`](crate::Filter::with_functions).
9
10use std::borrow::Cow;
11use std::sync::{Arc, LazyLock};
12
13use crate::FilterValue;
14
15#[macro_use]
16mod macros;
17
18pub(crate) mod trim;
19
20#[cfg(feature = "chrono")]
21mod ago;
22#[cfg(feature = "chrono")]
23mod datetime;
24#[cfg(feature = "chrono")]
25mod now;
26
27/// A function which can be called from within a filter expression using the
28/// familiar `name(args...)` syntax.
29///
30/// The crate provides a small set of built-in functions, but you can define
31/// your own by implementing this trait and registering instances with
32/// [`Filter::with_functions`](crate::Filter::with_functions). A function
33/// reports its [`name`](Function::name) and [`arity`](Function::arity) so the
34/// parser can resolve and validate calls up-front, and evaluates via
35/// [`call`](Function::call).
36///
37/// Implementations must be `Send + Sync` so that a parsed [`Filter`](crate::Filter)
38/// can be shared across threads.
39///
40/// ```
41/// use std::borrow::Cow;
42/// use filt_rs::{FilterValue, Function};
43///
44/// /// A `len(string)` function returning the number of characters in its argument.
45/// struct Len;
46///
47/// impl Function for Len {
48///     fn name(&self) -> &str {
49///         "len"
50///     }
51///
52///     fn arity(&self) -> usize {
53///         1
54///     }
55///
56///     fn call<'a>(&self, args: &[Cow<'a, FilterValue<'a>>]) -> Cow<'a, FilterValue<'a>> {
57///         match args[0].as_ref() {
58///             FilterValue::String(s) => Cow::Owned(FilterValue::Number(s.chars().count() as f64)),
59///             _ => Cow::Owned(FilterValue::Null),
60///         }
61///     }
62/// }
63///
64/// assert_eq!(Len.name(), "len");
65/// assert_eq!(Len.arity(), 1);
66/// ```
67pub trait Function: Send + Sync {
68    /// The name used to invoke this function in a filter expression.
69    fn name(&self) -> &str;
70
71    /// The exact number of arguments this function accepts.
72    ///
73    /// The parser checks a call's argument count against this when the filter is
74    /// parsed, so a mismatch fails fast with a friendly error rather than at
75    /// evaluation time.
76    fn arity(&self) -> usize;
77
78    /// Evaluates the function against its already-evaluated arguments.
79    ///
80    /// The slice is guaranteed to hold exactly [`arity`](Function::arity)
81    /// elements (the parser enforces this), so implementations may index into it
82    /// directly. Returning a value which *borrows* from the arguments — for
83    /// example a trimmed sub-slice of a borrowed string — keeps evaluation
84    /// allocation-free, mirroring the rest of the interpreter.
85    fn call<'a>(&self, args: &[Cow<'a, FilterValue<'a>>]) -> Cow<'a, FilterValue<'a>>;
86}
87
88/// Returns the shared set of built-in functions available in every filter,
89/// regardless of how it is constructed.
90///
91/// The set is built once and shared (reference-counted) thereafter, so cloning
92/// the returned handle is cheap.
93pub(crate) fn base_functions() -> Arc<[Arc<dyn Function>]> {
94    static BASE: LazyLock<Arc<[Arc<dyn Function>]>> = LazyLock::new(|| {
95        #[allow(unused_mut)]
96        let mut functions: Vec<Arc<dyn Function>> = vec![Arc::new(trim::trim)];
97
98        #[cfg(feature = "chrono")]
99        {
100            functions.push(Arc::new(now::now));
101            functions.push(Arc::new(ago::ago));
102            functions.push(Arc::new(datetime::datetime));
103        }
104
105        functions.into()
106    });
107
108    BASE.clone()
109}