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}