aimx/
function_registry.rs

1//! Function registry and calling interface.
2//!
3//! This module provides the function registry system that manages
4//! standalone functions and methods available in expressions.
5//! It also includes utilities for converting between [`Value`] types
6//! and native Rust types for function implementations.
7//!
8//! The function registry is a thread-safe singleton that uses [`OnceLock`] and [`RwLock`]
9//! to ensure one-time initialization with all built-in functions registered while
10//! allowing multiple concurrent readers.
11//!
12//! # Usage
13//!
14//! Functions are typically registered using the macros provided in [`crate::macros`]
15//! and are automatically available through the global registry:
16//!
17//! ```rust
18//! use aimx::{aimx_parse, ExpressionLike, Context};
19//!
20//! let mut context = Context::new();
21//! let expression = aimx_parse("sqrt(16) + abs(-5)");
22//! let result = expression.evaluate(&mut context).unwrap();
23//! assert_eq!(result.to_string(), "9");
24//! ```
25//!
26//! Custom functions can be registered with the registry:
27//!
28//! ```rust
29//! use aimx::{function_registry::FunctionRegistry, define_direct_function};
30//!
31//! // Get a write lock on the registry
32//! let mut registry = FunctionRegistry::write_lock().unwrap();
33//! 
34//! // Register a custom function
35//! define_direct_function!(
36//!     double,
37//!     args: [f64],
38//!     |x: f64| x * 2.0
39//! )(&mut *registry);
40//!
41//! // Drop the lock to allow other threads to access the registry
42//! drop(registry);
43//! ```
44//!
45//! Functions can be called as standalone functions or as methods on values:
46//!
47//! ```aimx
48//! // Standalone function call
49//! sqrt(16)
50//!
51//! // Method call on array
52//! (1, 2, 3).sum()
53//! ```
54
55use std::collections::HashMap;
56use std::sync::OnceLock;
57use std::sync::RwLock;
58use anyhow::{anyhow, Result};
59use crate::{
60    ContextLike,
61    Value,
62    Literal,
63    functions,
64};
65use jiff::civil::DateTime;
66
67/// Registry for managing function handlers.
68///
69/// The function registry maintains a collection of named function handlers
70/// that can be called during expression evaluation. It supports both
71/// standalone functions and methods called on values.
72///
73/// This registry is a thread-safe singleton that ensures one-time initialization
74/// with all built-in functions registered.
75pub struct FunctionRegistry {
76    handlers: HashMap<String, Box<dyn FunctionHandler>>,
77}
78
79/// Trait for function handlers.
80///
81/// This trait defines the interface that all function handlers must implement.
82/// Function handlers are responsible for executing the actual function logic
83/// and returning the result as a [`Value`].
84///
85/// This trait is automatically implemented when using the function registration
86/// macros in [`crate::macros`].
87pub trait FunctionHandler: Send + Sync {
88    /// Call the function with the provided arguments.
89    ///
90    /// # Arguments
91    ///
92    /// * `context` - The evaluation context for closures
93    /// * `args` - A slice of [`Value`] arguments passed to the function
94    ///
95    /// # Returns
96    ///
97    /// Returns a `Result<Value>` containing the function result or an error
98    /// if the function execution fails.
99    ///
100    /// # Note
101    ///
102    /// For method calls, the first argument is typically the value the method
103    /// is being called on, followed by any additional arguments.
104    fn call(&self, context: &mut dyn ContextLike, args: &[Value]) -> Result<Value>;
105}
106
107impl FunctionRegistry {
108    /// Get the global singleton instance of the function registry.
109    ///
110    /// This method uses [`OnceLock`] to ensure thread-safe one-time initialization
111    /// of the function registry with all built-in functions registered.
112    /// Uses [`RwLock`] to allow multiple concurrent readers while ensuring
113    /// exclusive access during writes.
114    ///
115    /// # Returns
116    ///
117    /// Returns a reference to the RwLock-protected singleton function registry.
118    ///
119    /// # Example
120    ///
121    /// ```
122    /// use aimx::function_registry::FunctionRegistry;
123    ///
124    /// let registry = FunctionRegistry::get_instance();
125    /// // Typically you'd use read_lock() or write_lock() instead of accessing directly
126    /// ```
127    pub fn get_instance() -> &'static RwLock<Self> {
128        static INSTANCE: OnceLock<RwLock<FunctionRegistry>> = OnceLock::new();
129        
130        INSTANCE.get_or_init(|| {
131            let mut registry = Self::new();
132            functions::register_all_functions(&mut registry);
133            RwLock::new(registry)
134        })
135    }
136
137    /// Get a read lock on the function registry for function calls.
138    ///
139    /// This is a convenience method that returns a read guard, allowing
140    /// multiple concurrent function calls without blocking each other.
141    ///
142    /// # Returns
143    ///
144    /// Returns a `Result` containing the read guard or an error if the lock is poisoned.
145    ///
146    /// # Example
147    ///
148    /// ```
149    /// use aimx::function_registry::FunctionRegistry;
150    ///
151    /// let registry = FunctionRegistry::read_lock().unwrap();
152    /// // Use registry for function calls
153    /// ```
154    pub fn read_lock() -> Result<std::sync::RwLockReadGuard<'static, Self>> {
155        Self::get_instance().read().map_err(|_| anyhow!("Function registry lock is poisoned"))
156    }
157
158    /// Get a write lock on the function registry for registration.
159    ///
160    /// This is a convenience method that returns a write guard, providing
161    /// exclusive access for registering new functions.
162    ///
163    /// # Returns
164    ///
165    /// Returns a `Result` containing the write guard or an error if the lock is poisoned.
166    ///
167    /// # Example
168    ///
169    /// ```
170    /// use aimx::{function_registry::FunctionRegistry, define_direct_function};
171    ///
172    /// let mut registry = FunctionRegistry::write_lock().unwrap();
173    /// define_direct_function!(
174    ///     custom_func,
175    ///     args: [f64],
176    ///     |x: f64| x * 2.0
177    /// )(&mut *registry);
178    /// // Drop the lock to allow other threads to access the registry
179    /// ```
180    pub fn write_lock() -> Result<std::sync::RwLockWriteGuard<'static, Self>> {
181        Self::get_instance().write().map_err(|_| anyhow!("Function registry lock is poisoned"))
182    }
183
184    /// Create a new, empty function registry.
185    ///
186    /// This is primarily used internally during initialization.
187    fn new() -> Self {
188        Self {
189            handlers: HashMap::new(),
190        }
191    }
192
193    /// Register a function handler with the given name.
194    ///
195    /// # Arguments
196    ///
197    /// * `name` - The name to register the function under
198    /// * `handler` - The function handler implementation
199    ///
200    /// # Example
201    ///
202    /// ```
203    /// use aimx::function_registry::{FunctionRegistry, FunctionHandler};
204    /// use aimx::{ContextLike, Value, Literal};
205    /// use anyhow::Result;
206    ///
207    /// struct CustomHandler;
208    /// 
209    /// impl FunctionHandler for CustomHandler {
210    ///     fn call(&self, context: &mut dyn ContextLike, args: &[Value]) -> Result<Value> {
211    ///         Ok(Value::from(42.0))
212    ///     }
213    /// }
214    /// 
215    /// // In practice, you would typically register functions through the write_lock() method
216    /// // This example shows the trait implementation for documentation purposes
217    /// ```
218    pub fn register<F>(&mut self, name: String, handler: F)
219    where
220        F: FunctionHandler + 'static,
221    {
222         self.handlers.insert(name, Box::new(handler));
223    }
224
225    /// Call a standalone function with the given name and argument.
226    ///
227    /// This method handles flattening array arguments into individual arguments
228    /// before passing them to the function handler.
229    ///
230    /// # Arguments
231    ///
232    /// * `context` - The evaluation context for closures
233    /// * `name` - The name of the function to call
234    /// * `arg` - The argument(s) to pass to the function (can be Empty, Literal, or Array)
235    ///
236    /// # Returns
237    ///
238    /// Returns a `Result<Value>` containing the function result or an error
239    /// if the function is not found or execution fails.
240    ///
241    /// # Example
242    ///
243    /// ```rust
244    /// # use aimx::{function_registry::FunctionRegistry, Context, Value, Literal};
245    /// # let mut context = Context::new();
246    /// # let registry = FunctionRegistry::read_lock().unwrap();
247    /// // Call a function with a single argument
248    /// let result = registry.function_call(&mut context, "abs", Value::from(-5.0));
249    ///
250    /// // Call a function with multiple arguments as an array
251    /// let args = Value::Array(vec![
252    ///     Box::new(Value::from(2.0)),
253    ///     Box::new(Value::from(3.0))
254    /// ]);
255    /// let result = registry.function_call(&mut context, "pow", args);
256    /// # assert!(result.is_ok());
257    /// ```
258    pub fn function_call(&self, context: &mut dyn ContextLike, name: &str, arg: Value) -> Result<Value> {
259        let mut args:Vec<Value> = Vec::new();
260        match arg {
261            Value::Literal(_) => args.push(arg),
262            Value::Array(array) => {
263                for box_value in array.into_iter() {
264                    let value = *box_value;
265                    args.push(value);
266                }
267            }
268            _ => {}
269        };
270        self.handlers
271            .get(name)
272            .ok_or_else(|| anyhow!("Function '{}' not found~{}", name, name))?
273            .call(context, &args)
274    }
275
276    /// Call a method on a value with the given name and argument.
277    ///
278    /// This method passes the target value as the first argument to the
279    /// function handler, followed by any additional arguments.
280    ///
281    /// # Arguments
282    ///
283    /// * `context` - The evaluation context for closures
284    /// * `name` - The name of the method to call
285    /// * `value` - The value on which to call the method
286    /// * `arg` - The argument(s) to pass to the method (can be Empty, Literal, or Array)
287    ///
288    /// # Returns
289    ///
290    /// Returns a `Result<Value>` containing the method result or an error
291    /// if the method is not found or execution fails.
292    ///
293    /// # Example
294    ///
295    /// ```rust
296    /// # use aimx::{function_registry::FunctionRegistry, Context, Value, Literal};
297    /// # let mut context = Context::new();
298    /// # let registry = FunctionRegistry::read_lock().unwrap();
299    /// // Call a method on an array value
300    /// let array_value = Value::Array(vec![
301    ///     Box::new(Value::from(1.0)),
302    ///     Box::new(Value::from(2.0)),
303    ///     Box::new(Value::from(3.0))
304    /// ]);
305    /// let result = registry.method_call(&mut context, "sum", array_value, Value::Array(vec![]));
306    /// # assert!(result.is_ok());
307    /// ```
308    pub fn method_call(&self, context: &mut dyn ContextLike, name: &str, value: Value, arg: Value) -> Result<Value> {
309        let mut args:Vec<Value> = Vec::new();
310        args.push(value);
311        match arg {
312            Value::Literal(_) => {
313                args.push(arg);
314            }
315            Value::Array(array) => {
316                for box_value in array.into_iter() {
317                    let value = *box_value;
318                    args.push(value);
319                }
320            }
321            _ => {}
322        };
323        self.handlers
324            .get(name)
325            .ok_or_else(|| anyhow!("Method '{}' not found~{}", name, name))?
326            .call(context, &args)
327    }
328}
329
330/// Convert a Value to a native Rust type based on the expected type T.
331///
332/// This utility function simplifies converting [`Value`] instances to
333/// native Rust types in function implementations.
334///
335/// This function is typically used internally by the function registration macros
336/// in [`crate::macros`] and is automatically called when functions are invoked.
337///
338/// # Arguments
339///
340/// * `value` - The value to convert
341///
342/// # Returns
343///
344/// Returns a `Result<T>` containing the converted value or an error
345/// if conversion fails.
346///
347/// # Example
348///
349/// ```
350/// use aimx::{function_registry::convert_value_to_type, Value, Literal};
351///
352/// let value = Value::from(42.0);
353/// let number: f64 = convert_value_to_type(&value).unwrap();
354/// assert_eq!(number, 42.0);
355/// ```
356pub fn convert_value_to_type<T>(value: &Value) -> Result<T> 
357where 
358    T: ValueConverter
359{
360    T::from_value(value)
361}
362
363/// Convert a native Rust type back to a Value.
364///
365/// This utility function simplifies converting native Rust types to
366/// [`Value`] instances for function return values.
367///
368/// This function is typically used internally by the function registration macros
369/// in [`crate::macros`] and is automatically called when functions return results.
370///
371/// # Arguments
372///
373/// * `result` - The value to convert
374///
375/// # Returns
376///
377/// Returns a `Result<Value>` containing the converted value.
378///
379/// # Example
380///
381/// ```
382/// use aimx::function_registry::convert_result_to_value;
383///
384/// let number = 42.0;
385/// let value = convert_result_to_value(number).unwrap();
386/// ```
387pub fn convert_result_to_value<T>(result: T) -> Result<Value>
388where
389    T: Into<Value>
390{
391    Ok(result.into())
392}
393
394/// Trait for converting Values to native types.
395///
396/// This trait enables automatic conversion between [`Value`] instances and
397/// native Rust types. It is implemented for common types like `f64`, `String`,
398/// `bool`, `DateTime`, and `Vec<T>`.
399///
400/// This trait is automatically implemented for common types and is typically
401/// used internally by the function registration macros in [`crate::macros`].
402pub trait ValueConverter: Sized {
403    /// Convert a Value to this native type.
404    ///
405    /// # Arguments
406    ///
407    /// * `value` - The value to convert
408    ///
409    /// # Returns
410    ///
411    /// Returns a `Result<Self>` containing the converted value or an error
412    /// if conversion fails.
413    fn from_value(value: &Value) -> Result<Self>;
414}
415
416// Implementations for common types
417impl ValueConverter for f64 {
418    fn from_value(value: &Value) -> Result<Self> {
419        value.to_number()
420    }
421}
422
423impl ValueConverter for String {
424    fn from_value(value: &Value) -> Result<Self> {
425        match value {
426            Value::Literal(Literal::Text(s)) => Ok(s.clone()),
427            Value::Literal(literal) => Ok(literal.to_string()),
428            Value::Array(_) => Ok(value.to_string()),
429            _ => Ok(String::new()),
430        }
431    }
432}
433
434impl ValueConverter for bool {
435    fn from_value(value: &Value) -> Result<Self> {
436        match value {
437            Value::Literal(literal) => Ok(literal.to_bool()),
438            Value::Array(array) => Ok(!array.is_empty()),
439            _ => Ok(false),
440        }
441    }
442}
443
444impl ValueConverter for DateTime {
445    fn from_value(value: &Value) -> Result<Self> {
446        match value {
447            Value::Literal(Literal::Date(dt)) => Ok(*dt),
448            Value::Literal(literal) => {
449                // Try to convert the literal to a date
450                literal.clone().as_date().and_then(|date_literal| {
451                    match date_literal {
452                        Literal::Date(dt) => Ok(dt),
453                        _ => Err(anyhow!("Failed to convert to DateTime"))
454                    }
455                })
456            },
457            _ => Err(anyhow!("Cannot convert {} to DateTime", value.type_as_string()))
458        }
459    }
460}
461
462impl<T: ValueConverter> ValueConverter for Vec<T> {
463    fn from_value(value: &Value) -> Result<Self> {
464        match value {
465            Value::Array(array) => {
466                array.iter().map(|box_value| {
467                    let value = box_value.as_ref();
468                    T::from_value(&value.clone())
469                }).collect()
470            },
471            _ => {
472                // For single values, create a single-element vector
473                Ok(vec![T::from_value(value)?])
474            }
475        }
476    }
477}
478
479impl From<f64> for Value {
480    fn from(value: f64) -> Self {
481        Value::Literal(Literal::Number(value))
482    }
483}
484
485impl From<String> for Value {
486    fn from(value: String) -> Self {
487        Value::Literal(Literal::Text(value))
488    }
489}
490
491impl From<bool> for Value {
492    fn from(value: bool) -> Self {
493        Value::Literal(Literal::Bool(value))
494    }
495}
496
497impl From<DateTime> for Value {
498    fn from(value: DateTime) -> Self {
499        Value::Literal(Literal::Date(value))
500    }
501}
502
503impl From<Vec<f64>> for Value {
504    fn from(value: Vec<f64>) -> Self {
505        Value::Array(value.into_iter().map(|value|Box::new(Value::Literal(Literal::Number(value)))).collect())
506    }
507}
508
509impl From<Vec<String>> for Value {
510    fn from(value: Vec<String>) -> Self {
511        Value::Array(value.into_iter().map(|value|Box::new(Value::Literal(Literal::Text(value)))).collect())
512    }
513}
514
515impl From<Vec<bool>> for Value {
516    fn from(value: Vec<bool>) -> Self {
517        Value::Array(value.into_iter().map(|value|Box::new(Value::Literal(Literal::Bool(value)))).collect())
518    }
519}
520
521impl From<Vec<DateTime>> for Value {
522    fn from(value: Vec<DateTime>) -> Self {
523        Value::Array(value.into_iter().map(|value|Box::new(Value::Literal(Literal::Date(value)))).collect())
524    }
525}
526
527#[cfg(test)]
528mod tests {
529    use super::*;
530    use crate::Literal;
531
532    #[test]
533    fn test_function_registry_singleton() {
534        let registry1 = FunctionRegistry::get_instance();
535        let registry2 = FunctionRegistry::get_instance();
536        assert!(std::ptr::eq(registry1, registry2));
537    }
538
539    #[test]
540    fn test_read_lock() {
541        let registry = FunctionRegistry::read_lock();
542        assert!(registry.is_ok());
543    }
544
545    #[test]
546    fn test_write_lock() {
547        let registry = FunctionRegistry::write_lock();
548        assert!(registry.is_ok());
549    }
550
551    #[test]
552    fn test_convert_value_to_type_f64() {
553        let value = Value::from(42.5);
554        let result: Result<f64> = convert_value_to_type(&value);
555        assert!(result.is_ok());
556        assert_eq!(result.unwrap(), 42.5);
557    }
558
559    #[test]
560    fn test_convert_value_to_type_string() {
561        let value = Value::from("hello".to_string());
562        let result: Result<String> = convert_value_to_type(&value);
563        assert!(result.is_ok());
564        assert_eq!(result.unwrap(), "hello");
565    }
566
567    #[test]
568    fn test_convert_value_to_type_bool() {
569        let value = Value::from(true);
570        let result: Result<bool> = convert_value_to_type(&value);
571        assert!(result.is_ok());
572        assert_eq!(result.unwrap(), true);
573    }
574
575    #[test]
576    fn test_convert_result_to_value() {
577        let result = convert_result_to_value(42.0);
578        assert!(result.is_ok());
579        match result.unwrap() {
580            Value::Literal(Literal::Number(n)) => assert_eq!(n, 42.0),
581            _ => panic!("Expected Number literal"),
582        }
583    }
584
585    #[test]
586    fn test_vec_conversion() {
587        let values = vec![Value::from(1.0), Value::from(2.0), Value::from(3.0)];
588        let array_value = Value::Array(values.into_iter().map(Box::new).collect());
589        
590        let result: Result<Vec<f64>> = convert_value_to_type(&array_value);
591        assert!(result.is_ok());
592        assert_eq!(result.unwrap(), vec![1.0, 2.0, 3.0]);
593    }
594}