use std::collections::HashMap;
use std::sync::Arc;
use uni_common::{Result, Value};
pub type CustomScalarFn = Arc<dyn Fn(&[Value]) -> Result<Value> + Send + Sync>;
pub const LEGACY_USER_PLUGIN_ID: &str = "user.legacy";
#[derive(Default, Clone)]
pub struct CustomFunctionRegistry {
functions: HashMap<String, CustomScalarFn>,
}
impl CustomFunctionRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, name: String, func: CustomScalarFn) {
self.functions.insert(name.to_uppercase(), func);
}
pub fn get(&self, name: &str) -> Option<&CustomScalarFn> {
self.functions.get(&name.to_uppercase())
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &CustomScalarFn)> {
self.functions.iter().map(|(k, v)| (k.as_str(), v))
}
pub fn remove(&mut self, name: &str) -> bool {
self.functions.remove(&name.to_uppercase()).is_some()
}
pub fn is_empty(&self) -> bool {
self.functions.is_empty()
}
}
impl std::fmt::Debug for CustomFunctionRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CustomFunctionRegistry")
.field("functions", &self.functions.keys().collect::<Vec<_>>())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn legacy_register_and_get_roundtrip() {
let mut reg = CustomFunctionRegistry::new();
let f: CustomScalarFn = Arc::new(|_args: &[Value]| Ok(Value::String("ok".to_owned())));
reg.register("MYFN".to_owned(), f);
assert!(reg.get("myfn").is_some());
assert!(reg.get("MYFN").is_some());
}
#[test]
fn legacy_remove_clears_entry() {
let mut reg = CustomFunctionRegistry::new();
let f: CustomScalarFn = Arc::new(|_| Ok(Value::Null));
reg.register("MYFN".to_owned(), f);
assert!(reg.remove("myfn"));
assert!(reg.get("MYFN").is_none());
}
#[test]
fn legacy_replace_updates_entry() {
let mut reg = CustomFunctionRegistry::new();
reg.register(
"MYFN".to_owned(),
Arc::new(|_| Ok(Value::String("first".to_owned()))),
);
reg.register(
"MYFN".to_owned(),
Arc::new(|_| Ok(Value::String("second".to_owned()))),
);
let v = (reg.get("MYFN").unwrap())(&[]).unwrap();
assert_eq!(v, Value::String("second".to_owned()));
}
}