use crate::error::Error;
use crate::types::Value;
use std::collections::HashMap;
pub trait CustomFunction: Send + Sync {
fn name(&self) -> &str;
fn min_args(&self) -> usize;
fn max_args(&self) -> Option<usize>;
fn execute(&self, args: Vec<Value>) -> Result<Value, Error>;
fn description(&self) -> Option<&str> { None }
fn example(&self) -> Option<&str> { None }
}
#[derive(Default)]
pub struct FunctionRegistry {
functions: HashMap<String, Box<dyn CustomFunction>>,
}
impl FunctionRegistry {
pub fn new() -> Self {
Self {
functions: HashMap::new(),
}
}
pub fn register(&mut self, function: Box<dyn CustomFunction>) -> Result<(), Error> {
let name = function.name().to_uppercase();
if name.is_empty() {
return Err(Error::new("Function name cannot be empty", None));
}
if function.min_args() > function.max_args().unwrap_or(usize::MAX) {
return Err(Error::new("min_args cannot be greater than max_args", None));
}
self.functions.insert(name, function);
Ok(())
}
pub fn get(&self, name: &str) -> Option<&dyn CustomFunction> {
self.functions.get(&name.to_uppercase()).map(|f| f.as_ref())
}
pub fn list_functions(&self) -> Vec<&str> {
self.functions.keys().map(|s| s.as_str()).collect()
}
pub fn unregister(&mut self, name: &str) -> bool {
self.functions.remove(&name.to_uppercase()).is_some()
}
pub fn has_function(&self, name: &str) -> bool {
self.functions.contains_key(&name.to_uppercase())
}
pub fn execute(&self, name: &str, args: Vec<Value>) -> Result<Value, Error> {
let function = self.get(name)
.ok_or_else(|| Error::new(format!("Unknown custom function: {}", name), None))?;
let arg_count = args.len();
if arg_count < function.min_args() {
return Err(Error::new(
format!("{} expects at least {} arguments, got {}",
name, function.min_args(), arg_count),
None
));
}
if let Some(max_args) = function.max_args() {
if arg_count > max_args {
return Err(Error::new(
format!("{} expects at most {} arguments, got {}",
name, max_args, arg_count),
None
));
}
}
function.execute(args)
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestFunction;
impl CustomFunction for TestFunction {
fn name(&self) -> &str { "TEST" }
fn min_args(&self) -> usize { 1 }
fn max_args(&self) -> Option<usize> { Some(2) }
fn execute(&self, args: Vec<Value>) -> Result<Value, Error> {
Ok(Value::String(format!("Called with {} args", args.len())))
}
fn description(&self) -> Option<&str> { Some("A test function") }
fn example(&self) -> Option<&str> { Some("TEST(1, 2)") }
}
#[test]
fn test_function_registry() {
let mut registry = FunctionRegistry::new();
assert!(registry.register(Box::new(TestFunction)).is_ok());
assert!(registry.has_function("TEST"));
assert!(registry.has_function("test"));
let result = registry.execute("TEST", vec![Value::Number(1.0)]).unwrap();
assert!(matches!(result, Value::String(_)));
assert!(registry.execute("TEST", vec![]).is_err()); assert!(registry.execute("TEST", vec![Value::Number(1.0), Value::Number(2.0), Value::Number(3.0)]).is_err());
assert!(registry.unregister("TEST"));
assert!(!registry.has_function("TEST"));
}
}