aimx/
macros.rs

1//! Macros for defining AIMX functions with type safety.
2//!
3//! This module provides three macros that simplify the process of registering
4//! functions with the AIMX function registry while maintaining type safety:
5//!
6//! - [`define_function!`](crate::define_function) - For functions that return `Result<T, E>` types
7//! - [`define_direct_function!`](crate::define_direct_function) - For functions that return direct values (not Result)
8//! - [`define_implicit_function!`](crate::define_implicit_function) - For methods that work with specific value types
9//!
10//! These macros abstract away the boilerplate code needed to implement the
11//! [`FunctionHandler`](crate::function_registry::FunctionHandler) trait and register functions with the registry.
12//!
13//! # Usage
14//!
15//! Functions are typically registered using closures that take a mutable reference
16//! to a [`FunctionRegistry`](crate::function_registry::FunctionRegistry). The macros are used within these registration closures:
17//!
18//! ```rust
19//! use aimx::{define_function, define_direct_function, function_registry::FunctionRegistry};
20//!
21//! fn register_example_functions(registry: &mut FunctionRegistry) {
22//!     // Define a function that returns a Result
23//!     define_function!(add, args: [f64, f64], |a: f64, b: f64| {
24//!         Ok(a + b)
25//!     })(registry);
26//!     
27//!     // Define a function that returns a direct value
28//!     define_direct_function!(multiply, args: [f64, f64], |a: f64, b: f64| {
29//!         a * b
30//!     })(registry);
31//! }
32//! ```
33//!
34//! # Design Principles
35//!
36//! The macros are designed with several key principles:
37//!
38//! - **Type Safety**: Automatic conversion between [`Value`](crate::Value) types and native Rust types
39//! - **Error Handling**: Proper integration with the anyhow error handling system
40//! - **Simplicity**: Minimal boilerplate for common function registration patterns
41//! - **Flexibility**: Support for functions with 0-4 arguments of any type
42//!
43//! See also: [`crate::function_registry`], [`crate::Value`], [`crate::function_registry::FunctionHandler`]
44
45/// Two-macro solution for better type safety and clarity
46///
47/// Macro for functions that return Result<T, E>
48/// Uses .map() conversion for Result types
49#[macro_export] 
50macro_rules! define_function {
51    // No arguments
52    ($name:ident, args: [], $body:expr) => {
53        |registry: &mut $crate::function_registry::FunctionRegistry| {
54            struct Handler;
55            impl $crate::function_registry::FunctionHandler for Handler {
56                fn call(&self, _context: &mut dyn $crate::ContextLike, _args: &[$crate::Value]) -> Result<$crate::Value, anyhow::Error> {
57                    let result: anyhow::Result<_> = $body;
58                    result.map(|v| v.into())
59                }
60            }
61            registry.register(
62                stringify!($name).to_string(),
63                Handler,
64            );
65        }
66    };
67    
68    // One argument
69    ($name:ident, args: [$arg_type:ty], $body:expr) => {
70        |registry: &mut $crate::function_registry::FunctionRegistry| {
71            struct Handler;
72            impl $crate::function_registry::FunctionHandler for Handler {
73                fn call(&self, _context: &mut dyn $crate::ContextLike, args: &[$crate::Value]) -> Result<$crate::Value, anyhow::Error> {
74                    if args.len() < 1 {
75                        return Err(anyhow::anyhow!("Expected 1 argument, got {}", args.len()));
76                    }
77                    let arg0 = $crate::function_registry::convert_value_to_type::<$arg_type>(&args[0])?;
78                    let result = $body(arg0);
79                    result.map(|v| v.into())
80                }
81            }
82            registry.register(
83                stringify!($name).to_string(),
84                Handler,
85            );
86        }
87    };
88    
89    // Two arguments
90    ($name:ident, args: [$arg_type1:ty, $arg_type2:ty], $body:expr) => {
91        |registry: &mut $crate::function_registry::FunctionRegistry| {
92            struct Handler;
93            impl $crate::function_registry::FunctionHandler for Handler {
94                fn call(&self, _context: &mut dyn $crate::ContextLike, args: &[$crate::Value]) -> Result<$crate::Value, anyhow::Error> {
95                    if args.len() < 2 {
96                        return Err(anyhow::anyhow!("Expected 2 arguments, got {}", args.len()));
97                    }
98                    let arg0 = $crate::function_registry::convert_value_to_type::<$arg_type1>(&args[0])?;
99                    let arg1 = $crate::function_registry::convert_value_to_type::<$arg_type2>(&args[1])?;
100                    let result = $body(arg0, arg1);
101                    result.map(|v| v.into())
102                }
103            }
104            registry.register(
105                stringify!($name).to_string(),
106                Handler,
107            );
108        }
109    };
110    
111    // Three arguments
112    ($name:ident, args: [$arg_type1:ty, $arg_type2:ty, $arg_type3:ty], $body:expr) => {
113        |registry: &mut $crate::function_registry::FunctionRegistry| {
114            struct Handler;
115            impl $crate::function_registry::FunctionHandler for Handler {
116                fn call(&self, _context: &mut dyn $crate::ContextLike, args: &[$crate::Value]) -> Result<$crate::Value, anyhow::Error> {
117                    if args.len() < 3 {
118                        return Err(anyhow::anyhow!("Expected 3 arguments, got {}", args.len()));
119                    }
120                    let arg0 = $crate::function_registry::convert_value_to_type::<$arg_type1>(&args[0])?;
121                    let arg1 = $crate::function_registry::convert_value_to_type::<$arg_type2>(&args[1])?;
122                    let arg2 = $crate::function_registry::convert_value_to_type::<$arg_type3>(&args[2])?;
123                    let result = $body(arg0, arg1, arg2);
124                    result.map(|v| v.into())
125                }
126            }
127            registry.register(
128                stringify!($name).to_string(),
129                Handler,
130            );
131        }
132    };
133    
134    // Four arguments
135    ($name:ident, args: [$arg_type1:ty, $arg_type2:ty, $arg_type3:ty, $arg_type4:ty], $body:expr) => {
136        |registry: &mut $crate::function_registry::FunctionRegistry| {
137            struct Handler;
138            impl $crate::function_registry::FunctionHandler for Handler {
139                fn call(&self, _context: &mut dyn $crate::ContextLike, args: &[$crate::Value]) -> Result<$crate::Value, anyhow::Error> {
140                    if args.len() < 4 {
141                        return Err(anyhow::anyhow!("Expected 4 arguments, got {}", args.len()));
142                    }
143                    let arg0 = $crate::function_registry::convert_value_to_type::<$arg_type1>(&args[0])?;
144                    let arg1 = $crate::function_registry::convert_value_to_type::<$arg_type2>(&args[1])?;
145                    let arg2 = $crate::function_registry::convert_value_to_type::<$arg_type3>(&args[2])?;
146                    let arg3 = $crate::function_registry::convert_value_to_type::<$arg_type4>(&args[3])?;
147                    let result = $body(arg0, arg1, arg2, arg3);
148                    result.map(|v| v.into())
149                }
150            }
151            registry.register(
152                stringify!($name).to_string(),
153                Handler,
154            );
155        }
156    };
157}
158
159/// Macro for functions that return direct values (not Result<T, E>)
160/// Uses convert_result_to_value() for direct value types
161#[macro_export]
162macro_rules! define_direct_function {
163    // No arguments
164    ($name:ident, args: [], $body:expr) => {
165        |registry: &mut $crate::function_registry::FunctionRegistry| {
166            struct Handler;
167            impl $crate::function_registry::FunctionHandler for Handler {
168                fn call(&self, _context: &mut dyn $crate::ContextLike, _args: &[$crate::Value]) -> Result<$crate::Value, anyhow::Error> {
169                    let result = $body;
170                    $crate::function_registry::convert_result_to_value(result)
171                }
172            }
173            registry.register(
174                stringify!($name).to_string(),
175                Handler,
176            );
177        }
178    };
179    
180    // One argument
181    ($name:ident, args: [$arg_type:ty], $body:expr) => {
182        |registry: &mut $crate::function_registry::FunctionRegistry| {
183            struct Handler;
184            impl $crate::function_registry::FunctionHandler for Handler {
185                fn call(&self, _context: &mut dyn $crate::ContextLike, args: &[$crate::Value]) -> Result<$crate::Value, anyhow::Error> {
186                    if args.len() < 1 {
187                        return Err(anyhow::anyhow!("Expected 1 argument, got {}", args.len()));
188                    }
189                    let arg0 = $crate::function_registry::convert_value_to_type::<$arg_type>(&args[0])?;
190                    let result = $body(arg0);
191                    $crate::function_registry::convert_result_to_value(result)
192                }
193            }
194            registry.register(
195                stringify!($name).to_string(),
196                Handler,
197            );
198        }
199    };
200    
201    // Two arguments
202    ($name:ident, args: [$arg_type1:ty, $arg_type2:ty], $body:expr) => {
203        |registry: &mut $crate::function_registry::FunctionRegistry| {
204            struct Handler;
205            impl $crate::function_registry::FunctionHandler for Handler {
206                fn call(&self, _context: &mut dyn $crate::ContextLike, args: &[$crate::Value]) -> Result<$crate::Value, anyhow::Error> {
207                    if args.len() < 2 {
208                        return Err(anyhow::anyhow!("Expected 2 arguments, got {}", args.len()));
209                    }
210                    let arg0 = $crate::function_registry::convert_value_to_type::<$arg_type1>(&args[0])?;
211                    let arg1 = $crate::function_registry::convert_value_to_type::<$arg_type2>(&args[1])?;
212                    let result = $body(arg0, arg1);
213                    $crate::function_registry::convert_result_to_value(result)
214                }
215            }
216            registry.register(
217                stringify!($name).to_string(),
218                Handler,
219            );
220        }
221    };
222    
223    // Three arguments
224    ($name:ident, args: [$arg_type1:ty, $arg_type2:ty, $arg_type3:ty], $body:expr) => {
225        |registry: &mut $crate::function_registry::FunctionRegistry| {
226            struct Handler;
227            impl $crate::function_registry::FunctionHandler for Handler {
228                fn call(&self, _context: &mut dyn $crate::ContextLike, args: &[$crate::Value]) -> Result<$crate::Value, anyhow::Error> {
229                    if args.len() < 3 {
230                        return Err(anyhow::anyhow!("Expected 3 arguments, got {}", args.len()));
231                    }
232                    let arg0 = $crate::function_registry::convert_value_to_type::<$arg_type1>(&args[0])?;
233                    let arg1 = $crate::function_registry::convert_value_to_type::<$arg_type2>(&args[1])?;
234                    let arg2 = $crate::function_registry::convert_value_to_type::<$arg_type3>(&args[2])?;
235                    let result = $body(arg0, arg1, arg2);
236                    $crate::function_registry::convert_result_to_value(result)
237                }
238            }
239            registry.register(
240                stringify!($name).to_string(),
241                Handler,
242            );
243        }
244    };
245    
246    // Four arguments
247    ($name:ident, args: [$arg_type1:ty, $arg_type2:ty, $arg_type3:ty, $arg_type4:ty], $body:expr) => {
248        |registry: &mut $crate::function_registry::FunctionRegistry| {
249            struct Handler;
250            impl $crate::function_registry::FunctionHandler for Handler {
251                fn call(&self, _context: &mut dyn $crate::ContextLike, args: &[$crate::Value]) -> Result<$crate::Value, anyhow::Error> {
252                    if args.len() < 4 {
253                        return Err(anyhow::anyhow!("Expected 4 arguments, got {}", args.len()));
254                    }
255                    let arg0 = $crate::function_registry::convert_value_to_type::<$arg_type1>(&args[0])?;
256                    let arg1 = $crate::function_registry::convert_value_to_type::<$arg_type2>(&args[1])?;
257                    let arg2 = $crate::function_registry::convert_value_to_type::<$arg_type3>(&args[2])?;
258                    let arg3 = $crate::function_registry::convert_value_to_type::<$arg_type4>(&args[3])?;
259                    let result = $body(arg0, arg1, arg2, arg3);
260                    $crate::function_registry::convert_result_to_value(result)
261                }
262            }
263            registry.register(
264                stringify!($name).to_string(),
265                Handler,
266            );
267        }
268    };
269}
270
271/// Macro for defining implicit functions that work with specific value types.
272///
273/// This macro provides automatic type conversion for method-style function calls,
274/// checking if values are already of the correct type or converting them if needed.
275/// It's particularly useful for defining methods that operate on specific value types
276/// like arrays or closures.
277///
278/// The key difference from other macros is that it provides automatic type checking
279/// and conversion for method calls where the first argument is the value the method
280/// is being called on.
281#[macro_export]
282macro_rules! define_implicit_function {
283    // One argument - for functions like array.len()
284    ($name:ident, args: [$arg_type0:path], $body:expr) => {
285        |registry: &mut $crate::function_registry::FunctionRegistry| {
286            struct Handler;
287            impl $crate::function_registry::FunctionHandler for Handler {
288                fn call(&self, _context: &mut dyn $crate::ContextLike, args: &[$crate::Value]) -> Result<$crate::Value, anyhow::Error> {
289                    if args.len() < 1 {
290                        return Err(anyhow::anyhow!("Expected 1 argument, got {}", args.len()));
291                    }
292                    let arg0 = if args[0].is_actual_type(&$arg_type0) { 
293                        &args[0] 
294                    } else { 
295                        &args[0].clone().to_type(&$arg_type0)? 
296                    };
297                    $body(arg0)
298                }
299            }
300            registry.register(
301                stringify!($name).to_string(),
302                Handler,
303            );
304        }
305    };
306    
307    // Two arguments - for functions like array.map(closure)
308    ($name:ident, args: [$arg_type0:path, $arg_type1:path], $body:expr) => {
309        |registry: &mut $crate::function_registry::FunctionRegistry| {
310            struct Handler;
311            impl $crate::function_registry::FunctionHandler for Handler {
312                fn call(&self, context: &mut dyn $crate::ContextLike, args: &[$crate::Value]) -> Result<$crate::Value, anyhow::Error> {
313                    if args.len() < 2 {
314                        return Err(anyhow::anyhow!("Expected 2 arguments, got {}", args.len()));
315                    }
316                    let arg0 = if args[0].is_actual_type(&$arg_type0) { 
317                        &args[0] 
318                    } else { 
319                        &args[0].clone().to_type(&$arg_type0)? 
320                    };
321                    let arg1 = if args[1].is_actual_type(&$arg_type1) { 
322                        &args[1] 
323                    } else { 
324                        &args[1].clone().to_type(&$arg_type1)? 
325                    };
326                    $body(context, arg0, arg1)
327                }
328            }
329            registry.register(
330                stringify!($name).to_string(),
331                Handler,
332            );
333        }
334    };
335    
336    // Three arguments
337    ($name:ident, args: [$arg_type0:path, $arg_type1:path, $arg_type2:path], $body:expr) => {
338        |registry: &mut $crate::function_registry::FunctionRegistry| {
339            struct Handler;
340            impl $crate::function_registry::FunctionHandler for Handler {
341                fn call(&self, context: &mut dyn $crate::ContextLike, args: &[$crate::Value]) -> Result<$crate::Value, anyhow::Error> {
342                    if args.len() < 3 {
343                        return Err(anyhow::anyhow!("Expected 3 arguments, got {}", args.len()));
344                    }
345                    let arg0 = if args[0].is_actual_type(&$arg_type0) { 
346                        &args[0] 
347                    } else { 
348                        &args[0].clone().to_type(&$arg_type0)? 
349                    };
350                    let arg1 = if args[1].is_actual_type(&$arg_type1) { 
351                        &args[1] 
352                    } else { 
353                        &args[1].clone().to_type(&$arg_type1)? 
354                    };
355                    let arg2 = if args[2].is_actual_type(&$arg_type2) { 
356                        &args[2] 
357                    } else { 
358                        &args[2].clone().to_type(&$arg_type2)? 
359                    };
360                    $body(context, arg0, arg1, arg2)
361                }
362            }
363            registry.register(
364                stringify!($name).to_string(),
365                Handler,
366            );
367        }
368    };
369    
370    // Four arguments
371    ($name:ident, args: [$arg_type0:path, $arg_type1:path, $arg_type2:path, $arg_type3:path], $body:expr) => {
372        |registry: &mut $crate::function_registry::FunctionRegistry| {
373            struct Handler;
374            impl $crate::function_registry::FunctionHandler for Handler {
375                fn call(&self, context: &mut dyn $crate::ContextLike, args: &[$crate::Value]) -> Result<$crate::Value, anyhow::Error> {
376                    if args.len() < 4 {
377                        return Err(anyhow::anyhow!("Expected 4 arguments, got {}", args.len()));
378                    }
379                    let arg0 = if args[0].is_actual_type(&$arg_type0) { 
380                        &args[0] 
381                    } else { 
382                        &args[0].clone().to_type(&$arg_type0)? 
383                    };
384                    let arg1 = if args[1].is_actual_type(&$arg_type1) { 
385                        &args[1] 
386                    } else { 
387                        &args[1].clone().to_type(&$arg_type1)? 
388                    };
389                    let arg2 = if args[2].is_actual_type(&$arg_type2) { 
390                        &args[2] 
391                    } else { 
392                        &args[2].clone().to_type(&$arg_type2)? 
393                    };
394                    let arg3 = if args[3].is_actual_type(&$arg_type3) { 
395                        &args[3] 
396                    } else { 
397                        &args[3].clone().to_type(&$arg_type3)? 
398                    };
399                    $body(context, arg0, arg1, arg2, arg3)
400                }
401            }
402            registry.register(
403                stringify!($name).to_string(),
404                Handler,
405            );
406        }
407    };
408}