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}