use std::collections::HashMap;
use std::sync::Arc;
use super::method::{self, MethodDescriptor, MethodRegistry};
use super::{AggregateFunction, IndexFunction, ProjectionFunction, ScalarFunction, builtin};
#[derive(Debug)]
pub struct FunctionRegistry {
functions: HashMap<&'static str, Arc<dyn ScalarFunction>>,
aggregates: HashMap<&'static str, Arc<dyn AggregateFunction>>,
projections: HashMap<&'static str, Arc<dyn ProjectionFunction>>,
index_functions: HashMap<&'static str, Arc<dyn IndexFunction>>,
methods: MethodRegistry,
}
impl FunctionRegistry {
pub fn new() -> Self {
Self {
functions: HashMap::new(),
aggregates: HashMap::new(),
projections: HashMap::new(),
index_functions: HashMap::new(),
methods: MethodRegistry::default(),
}
}
pub fn register(&mut self, func: impl ScalarFunction + 'static) {
let name = func.name();
self.functions.insert(name, Arc::new(func));
}
pub fn get(&self, name: &str) -> Option<&Arc<dyn ScalarFunction>> {
self.functions.get(name)
}
#[cfg(test)]
pub fn contains(&self, name: &str) -> bool {
self.functions.contains_key(name)
}
#[cfg(test)]
pub fn len(&self) -> usize {
self.functions.len()
}
pub fn register_aggregate(&mut self, func: impl AggregateFunction + 'static) {
let name = func.name();
self.aggregates.insert(name, Arc::new(func));
}
pub fn get_aggregate(&self, name: &str) -> Option<&Arc<dyn AggregateFunction>> {
self.aggregates.get(name)
}
#[cfg(test)]
pub fn is_aggregate(&self, name: &str) -> bool {
self.aggregates.contains_key(name)
}
#[cfg(test)]
pub fn aggregate_len(&self) -> usize {
self.aggregates.len()
}
pub fn get_count_aggregate(&self, has_arguments: bool) -> Arc<dyn AggregateFunction> {
if has_arguments {
Arc::new(builtin::aggregates::CountField)
} else {
Arc::clone(self.aggregates.get("count").expect("count should be registered"))
}
}
pub fn register_projection(&mut self, func: impl ProjectionFunction + 'static) {
let name = func.name();
self.projections.insert(name, Arc::new(func));
}
pub fn get_projection(&self, name: &str) -> Option<&Arc<dyn ProjectionFunction>> {
self.projections.get(name)
}
pub fn is_projection(&self, name: &str) -> bool {
self.projections.contains_key(name)
}
#[cfg(test)]
pub fn projection_len(&self) -> usize {
self.projections.len()
}
pub fn register_index_function(&mut self, func: impl IndexFunction + 'static) {
let name = func.name();
self.index_functions.insert(name, Arc::new(func));
}
pub fn get_index_function(&self, name: &str) -> Option<&Arc<dyn IndexFunction>> {
self.index_functions.get(name)
}
pub fn is_index_function(&self, name: &str) -> bool {
self.index_functions.contains_key(name)
}
pub fn get_method(&self, name: &str) -> Option<&Arc<MethodDescriptor>> {
self.methods.get(name)
}
pub fn with_builtins() -> Self {
let mut registry = Self::new();
builtin::register_all(&mut registry);
registry.methods = method::build_method_registry(®istry);
registry
}
}
impl Default for FunctionRegistry {
fn default() -> Self {
Self::with_builtins()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builtin_registry() {
let registry = FunctionRegistry::with_builtins();
assert!(registry.contains("math::abs"));
assert!(registry.contains("string::len"));
assert!(registry.contains("array::len"));
assert!(!registry.contains("unknown::function"));
assert!(registry.len() > 100);
}
#[test]
fn test_function_lookup() {
let registry = FunctionRegistry::with_builtins();
let abs = registry.get("math::abs").expect("math::abs should exist");
assert_eq!(abs.name(), "math::abs");
assert!(abs.is_pure());
assert!(!abs.is_async());
}
#[test]
fn test_aggregate_functions() {
let registry = FunctionRegistry::with_builtins();
assert!(registry.is_aggregate("count"));
assert!(registry.is_aggregate("math::sum"));
assert!(registry.is_aggregate("math::mean"));
assert!(registry.is_aggregate("math::min"));
assert!(registry.is_aggregate("math::max"));
assert!(registry.is_aggregate("math::stddev"));
assert!(registry.is_aggregate("math::variance"));
assert!(registry.is_aggregate("time::min"));
assert!(registry.is_aggregate("time::max"));
assert!(registry.is_aggregate("array::group"));
assert!(registry.is_aggregate("array::distinct"));
assert!(!registry.is_aggregate("math::abs"));
assert!(!registry.is_aggregate("string::len"));
assert!(registry.aggregate_len() >= 10);
}
#[test]
fn test_aggregate_lookup() {
let registry = FunctionRegistry::with_builtins();
let mean = registry.get_aggregate("math::mean").expect("math::mean should exist");
assert_eq!(mean.name(), "math::mean");
let _accumulator = mean.create_accumulator();
}
#[test]
fn test_projection_functions() {
let registry = FunctionRegistry::with_builtins();
assert!(registry.is_projection("type::field"));
assert!(registry.is_projection("type::fields"));
assert!(!registry.is_projection("math::abs"));
assert!(!registry.is_projection("string::len"));
assert!(!registry.is_projection("type::string"));
assert!(!registry.is_projection("count"));
assert!(!registry.is_projection("math::sum"));
assert_eq!(registry.projection_len(), 2);
}
#[test]
fn test_projection_lookup() {
let registry = FunctionRegistry::with_builtins();
let field = registry.get_projection("type::field").expect("type::field should exist");
assert_eq!(field.name(), "type::field");
let fields = registry.get_projection("type::fields").expect("type::fields should exist");
assert_eq!(fields.name(), "type::fields");
}
}