use crate::types::Value;
use crate::error::Error;
use std::collections::HashSet;
use super::{arithmetic, logical, string, array, datetime, financial, statistical};
pub struct FunctionDispatch {
arithmetic_functions: HashSet<&'static str>,
logical_functions: HashSet<&'static str>,
string_functions: HashSet<&'static str>,
array_functions: HashSet<&'static str>,
datetime_functions: HashSet<&'static str>,
financial_functions: HashSet<&'static str>,
statistical_functions: HashSet<&'static str>,
json_functions: HashSet<&'static str>,
}
impl FunctionDispatch {
pub fn new() -> Self {
let mut arithmetic_functions = HashSet::new();
arithmetic_functions.insert("SUM");
arithmetic_functions.insert("AVG");
arithmetic_functions.insert("AVERAGE");
arithmetic_functions.insert("MIN");
arithmetic_functions.insert("MAX");
arithmetic_functions.insert("ROUND");
arithmetic_functions.insert("CEIL");
arithmetic_functions.insert("CEILING");
arithmetic_functions.insert("FLOOR");
arithmetic_functions.insert("ABS");
arithmetic_functions.insert("SQRT");
arithmetic_functions.insert("POW");
arithmetic_functions.insert("POWER");
arithmetic_functions.insert("MOD");
arithmetic_functions.insert("INT");
arithmetic_functions.insert("PRODUCT");
arithmetic_functions.insert("MULTIPLY");
let mut logical_functions = HashSet::new();
logical_functions.insert("AND");
logical_functions.insert("OR");
logical_functions.insert("NOT");
logical_functions.insert("XOR");
logical_functions.insert("IF");
logical_functions.insert("IFS");
let mut string_functions = HashSet::new();
string_functions.insert("LENGTH");
string_functions.insert("CONCAT");
string_functions.insert("UPPER");
string_functions.insert("LOWER");
string_functions.insert("TRIM");
string_functions.insert("SUBSTRING");
string_functions.insert("SPLIT");
string_functions.insert("REPLACE");
string_functions.insert("SUBSTITUTE");
string_functions.insert("SUBSTITUTEM");
string_functions.insert("ISBLANK");
string_functions.insert("ISNUMBER");
string_functions.insert("ISTEXT");
string_functions.insert("INCLUDES");
string_functions.insert("LEFT");
string_functions.insert("RIGHT");
string_functions.insert("MID");
let mut array_functions = HashSet::new();
array_functions.insert("ARRAY");
array_functions.insert("FLATTEN");
array_functions.insert("FIRST");
array_functions.insert("LAST");
array_functions.insert("CONTAINS");
array_functions.insert("IN");
array_functions.insert("COUNT");
array_functions.insert("UNIQUE");
array_functions.insert("SORT");
array_functions.insert("REVERSE");
array_functions.insert("JOIN");
array_functions.insert("MERGE");
let mut datetime_functions = HashSet::new();
datetime_functions.insert("NOW");
datetime_functions.insert("DATE");
datetime_functions.insert("TIME");
datetime_functions.insert("YEAR");
datetime_functions.insert("MONTH");
datetime_functions.insert("DAY");
datetime_functions.insert("DATEADD");
datetime_functions.insert("DATEDIFF");
let mut financial_functions = HashSet::new();
financial_functions.insert("PMT");
financial_functions.insert("DB");
financial_functions.insert("FV");
financial_functions.insert("IPMT");
let mut statistical_functions = HashSet::new();
statistical_functions.insert("MEDIAN");
statistical_functions.insert("MODE.SNGL");
statistical_functions.insert("MODESNGL");
statistical_functions.insert("MODE_SNGL");
statistical_functions.insert("STDEV.P");
statistical_functions.insert("STDEVP");
statistical_functions.insert("STDEV_P");
statistical_functions.insert("VAR.P");
statistical_functions.insert("VARP");
statistical_functions.insert("VAR_P");
statistical_functions.insert("PERCENTILE.INC");
statistical_functions.insert("PERCENTILEINC");
statistical_functions.insert("PERCENTILE_INC");
statistical_functions.insert("QUARTILE.INC");
statistical_functions.insert("QUARTILEINC");
statistical_functions.insert("QUARTILE_INC");
let mut json_functions = HashSet::new();
json_functions.insert("DIG");
Self {
arithmetic_functions,
logical_functions,
string_functions,
array_functions,
datetime_functions,
financial_functions,
statistical_functions,
json_functions,
}
}
pub fn execute(&self, name: &str, args: &[Value]) -> Result<Value, Error> {
if self.array_functions.contains(name) {
return array::exec_array(name, args);
}
if self.arithmetic_functions.contains(name) {
return arithmetic::exec_arithmetic(name, args);
}
if self.logical_functions.contains(name) {
return logical::exec_logical(name, args);
}
if self.string_functions.contains(name) {
return string::exec_string(name, args);
}
if self.datetime_functions.contains(name) {
return datetime::exec_datetime(name, args);
}
if self.financial_functions.contains(name) {
return financial::exec_financial(name, args);
}
if self.statistical_functions.contains(name) {
return statistical::exec_statistical(name, args);
}
if self.json_functions.contains(name) {
return crate::runtime::json::exec_json(name, args);
}
Err(Error::new(format!("Unknown function: {}", name), None))
}
pub fn has_function(&self, name: &str) -> bool {
self.arithmetic_functions.contains(name) ||
self.logical_functions.contains(name) ||
self.string_functions.contains(name) ||
self.array_functions.contains(name) ||
self.datetime_functions.contains(name) ||
self.financial_functions.contains(name) ||
self.statistical_functions.contains(name) ||
self.json_functions.contains(name)
}
pub fn count(&self) -> usize {
self.arithmetic_functions.len() +
self.logical_functions.len() +
self.string_functions.len() +
self.array_functions.len() +
self.datetime_functions.len() +
self.financial_functions.len() +
self.statistical_functions.len() +
self.json_functions.len()
}
}
impl Default for FunctionDispatch {
fn default() -> Self {
Self::new()
}
}
lazy_static::lazy_static! {
static ref GLOBAL_DISPATCH: FunctionDispatch = FunctionDispatch::new();
}
pub fn exec_builtin_fast(name: &str, args: &[Value]) -> Result<Value, Error> {
GLOBAL_DISPATCH.execute(name, args)
}
pub fn has_builtin_function(name: &str) -> bool {
GLOBAL_DISPATCH.has_function(name)
}
pub fn builtin_function_count() -> usize {
GLOBAL_DISPATCH.count()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_function_dispatch_basic() {
let dispatch = FunctionDispatch::new();
assert!(dispatch.has_function("SUM"));
assert!(dispatch.has_function("UPPER"));
assert!(!dispatch.has_function("NONEXISTENT"));
}
#[test]
fn test_category_lookup() {
let dispatch = FunctionDispatch::new();
assert!(dispatch.arithmetic_functions.contains("SUM"));
assert!(dispatch.string_functions.contains("UPPER"));
assert!(dispatch.array_functions.contains("FLATTEN"));
assert!(dispatch.datetime_functions.contains("NOW"));
assert!(dispatch.financial_functions.contains("PMT"));
assert!(dispatch.statistical_functions.contains("MEDIAN"));
}
#[test]
fn test_global_dispatch() {
assert!(has_builtin_function("SUM"));
assert!(!has_builtin_function("NONEXISTENT"));
assert!(builtin_function_count() > 50); }
}