aimx/functions/
function_registry.rs

1//! Function registry and value conversion utilities.
2//!
3//! Provides the global registry for AIMX functions/methods and helpers
4//! used by registration macros to convert between [`Value`] and native types.
5//! The registry is initialized once with built-ins via [`functions::register_all_functions`]
6//! and accessed through a thread-safe [`RwLock`].
7use crate::{aim::ContextLike, functions, literals::Literal, values::Value};
8use anyhow::{Result, anyhow};
9use jiff::civil::DateTime;
10use std::collections::HashMap;
11use std::sync::OnceLock;
12use std::sync::{Arc, RwLock};
13
14/// Global registry mapping function names to handlers used during expression evaluation.
15/// Supports both standalone calls and method-style calls on [`Value`] receivers.
16pub struct FunctionRegistry {
17    handlers: HashMap<Arc<str>, Box<dyn FunctionHandler>>,
18}
19
20/// Handler invoked for a registered function or method.
21pub trait FunctionHandler: Send + Sync {
22    /// Call the function with the provided arguments.
23    ///
24    /// # Arguments
25    ///
26    /// * `context` - The evaluation context for closures
27    /// * `args` - A slice of [`Value`] arguments passed to the function
28    ///
29    /// # Returns
30    ///
31    /// Returns a `Result<Value>` containing the function result or an error
32    /// if the function execution fails.
33    ///
34    /// # Note
35    ///
36    /// For method calls, the first argument is typically the value the method
37    /// is being called on, followed by any additional arguments.
38    fn call(&self, context: &mut dyn ContextLike, args: Vec<Arc<Value>>) -> Result<Value>;
39}
40
41impl FunctionRegistry {
42    /// Get the global singleton instance of the function registry.
43    ///
44    /// This method uses [`OnceLock`] to ensure thread-safe one-time initialization
45    /// of the function registry with all built-in functions registered.
46    /// Uses [`RwLock`] to allow multiple concurrent readers while ensuring
47    /// exclusive access during writes.
48    ///
49    /// # Returns
50    ///
51    /// Returns a reference to the RwLock-protected singleton function registry.
52    pub fn get_instance() -> &'static RwLock<Self> {
53        static INSTANCE: OnceLock<RwLock<FunctionRegistry>> = OnceLock::new();
54
55        INSTANCE.get_or_init(|| {
56            let mut registry = Self::new();
57            functions::register_all_functions(&mut registry);
58            RwLock::new(registry)
59        })
60    }
61
62    /// Get a read lock on the function registry for function calls.
63    ///
64    /// This is a convenience method that returns a read guard, allowing
65    /// multiple concurrent function calls without blocking each other.
66    ///
67    /// # Returns
68    ///
69    /// Returns a `Result` containing the read guard or an error if the lock is poisoned.
70    pub fn read_lock() -> Result<std::sync::RwLockReadGuard<'static, Self>> {
71        Self::get_instance()
72            .read()
73            .map_err(|_| anyhow!("Function registry lock is poisoned"))
74    }
75
76    /// Get a write lock on the function registry for registration.
77    ///
78    /// This is a convenience method that returns a write guard, providing
79    /// exclusive access for registering new functions.
80    ///
81    /// # Returns
82    ///
83    /// Returns a `Result` containing the write guard or an error if the lock is poisoned.
84    pub fn write_lock() -> Result<std::sync::RwLockWriteGuard<'static, Self>> {
85        Self::get_instance()
86            .write()
87            .map_err(|_| anyhow!("Function registry lock is poisoned"))
88    }
89
90    /// Create a new, empty function registry.
91    ///
92    /// This is primarily used internally during initialization.
93    fn new() -> Self {
94        Self {
95            handlers: HashMap::new(),
96        }
97    }
98
99    /// Register a function handler with the given name.
100    ///
101    /// # Arguments
102    ///
103    /// * `name` - The name to register the function under
104    /// * `handler` - The function handler implementation
105    ///
106    pub fn register<F>(&mut self, name: Arc<str>, handler: F)
107    where
108        F: FunctionHandler + 'static,
109    {
110        self.handlers.insert(name, Box::new(handler));
111    }
112
113    /// Call with a read lock on the global registry. Flattens `Array` args.
114
115    pub fn function_call(
116        &self,
117        context: &mut dyn ContextLike,
118        name: Arc<str>,
119        arg: Arc<Value>,
120    ) -> Result<Arc<Value>> {
121        let mut args: Vec<Arc<Value>> = Vec::new();
122        match &*arg {
123            Value::Literal(_) => args.push(arg),
124            Value::Array(array) => {
125                for value in array.iter() {
126                    args.push(value.clone());
127                }
128            }
129            _ => {}
130        };
131        let result = self.handlers
132            .get(&name)
133            .ok_or_else(|| anyhow!("Function '{}' not found~{}", name, name))?
134            .call(context, args)?;
135        Ok(Arc::new(result))
136    }
137
138    /// Call a method handler with `value` prepended to args. Flattens `Array` args.
139
140    pub fn method_call(
141        &self,
142        context: &mut dyn ContextLike,
143        name: Arc<str>,
144        value: Arc<Value>,
145        arg: Arc<Value>,
146    ) -> Result<Arc<Value>> {
147        let mut args: Vec<Arc<Value>> = Vec::new();
148        args.push(value);
149        match &*arg {
150            Value::Literal(_) => {
151                args.push(arg);
152            }
153            Value::Array(array) => {
154                for value in array.iter() {
155                    args.push(value.clone());
156                }
157            }
158            _ => {}
159        };
160        let result = self.handlers
161            .get(&name)
162            .ok_or_else(|| anyhow!("Method '{}' not found~{}", name, name))?
163            .call(context, args)?;
164        Ok(Arc::new(result))
165    }
166
167    /// Returns `true` if a handler with `name` is registered.
168
169    pub fn handler_exists(&self, name: Arc<str>) -> bool {
170        self.handlers.contains_key(&name)
171    }
172}
173
174/// Convert a [`Value`] to `T` via [`ValueConverter`].
175pub fn convert_value_to_type<T>(value: Arc<Value>) -> Result<T>
176where
177    T: ValueConverter,
178{
179    T::from_value(value)
180}
181
182/// Convert any `T: Into<Value>` to [`Arc<Value>`].
183pub fn convert_result_to_value<T>(result: T) -> Result<Arc<Value>>
184where
185    T: Into<Value>,
186{
187    Ok(Arc::new(result.into()))
188}
189
190/// Convert [`Value`] to native types used by functions.
191
192pub trait ValueConverter: Sized {
193    /// Convert a Value to this native type.
194    ///
195    /// # Arguments
196    ///
197    /// * `value` - The value to convert
198    ///
199    /// # Returns
200    ///
201    /// Returns a `Result<Self>` containing the converted value or an error
202    /// if conversion fails.
203    fn from_value(value: Arc<Value>) -> Result<Self>;
204}
205
206// Implementations for common types
207impl ValueConverter for f64 {
208    fn from_value(value: Arc<Value>) -> Result<Self> {
209        value.to_number()
210    }
211}
212
213impl ValueConverter for Arc<str> {
214    fn from_value(value: Arc<Value>) -> Result<Self> {
215        Ok(value.to_arc_str())
216    }
217}
218
219impl ValueConverter for bool {
220    fn from_value(value: Arc<Value>) -> Result<Self> {
221        match &*value {
222            Value::Literal(literal) => Ok(literal.to_bool()),
223            Value::Array(array) => Ok(!array.is_empty()),
224            _ => Ok(false),
225        }
226    }
227}
228
229impl ValueConverter for DateTime {
230    fn from_value(value: Arc<Value>) -> Result<Self> {
231        match &*value {
232            Value::Literal(Literal::Date(dt)) => Ok(*dt),
233            Value::Literal(literal) => {
234                // Try to convert the literal to a date
235                literal
236                    .clone()
237                    .as_date()
238                    .and_then(|date_literal| match date_literal {
239                        Literal::Date(dt) => Ok(dt),
240                        _ => Err(anyhow!("Failed to convert to DateTime")),
241                    })
242            }
243            _ => Err(anyhow!(
244                "Cannot convert {} to DateTime",
245                value.type_as_string()
246            )),
247        }
248    }
249}
250
251impl<T: ValueConverter> ValueConverter for Vec<Arc<T>> {
252    fn from_value(value: Arc<Value>) -> Result<Self> {
253        match &*value {
254            Value::Array(array) => {
255                // For arrays, we need to convert each Arc<Value> to Arc<T>
256                // This is a complex conversion that requires creating new T instances
257                array
258                    .iter()
259                    .map(|value| {
260                        T::from_value(value.clone()).map(Arc::new)
261                    })
262                    .collect()
263            }
264            _ => {
265                // For single values, create a single-element vector
266                T::from_value(value).map(|t| vec![Arc::new(t)])
267            }
268        }
269    }
270}
271
272impl ValueConverter for Vec<Arc<str>> {
273    fn from_value(value: Arc<Value>) -> Result<Self> {
274        match &*value {
275            Value::Array(array) => {
276                // Convert each Arc<Value> to Arc<str>
277                array
278                    .iter()
279                    .map(|value| {
280                        Ok(value.to_arc_str())
281                    })
282                    .collect()
283            }
284            _ => {
285                // For single values, create a single-element vector
286                Ok(vec![value.to_arc_str()])
287            }
288        }
289    }
290}
291
292impl ValueConverter for Vec<f64> {
293    fn from_value(value: Arc<Value>) -> Result<Self> {
294        match &*value {
295            Value::Array(array) => {
296                // Convert each Arc<Value> to f64
297                array
298                    .iter()
299                    .map(|value| {
300                        f64::from_value(value.clone())
301                    })
302                    .collect()
303            }
304            _ => {
305                // For single values, create a single-element vector
306                f64::from_value(value).map(|n| vec![n])
307            }
308        }
309    }
310}