use std::sync::LazyLock;
#[derive(Debug, Clone, Copy)]
pub struct FunctionPropertySpec {
pub entity_args: &'static [usize],
pub property_name_args: &'static [(usize, usize)],
pub needs_full_entity: bool,
}
static FUNCTION_SPECS: LazyLock<std::collections::HashMap<&'static str, FunctionPropertySpec>> =
LazyLock::new(|| {
let full_entity = FunctionPropertySpec {
entity_args: &[0],
property_name_args: &[],
needs_full_entity: true,
};
let entity_arg_only = FunctionPropertySpec {
entity_args: &[0],
property_name_args: &[],
needs_full_entity: false,
};
let no_entity = FunctionPropertySpec {
entity_args: &[],
property_name_args: &[],
needs_full_entity: false,
};
std::collections::HashMap::from([
(
"UNI.TEMPORAL.VALIDAT",
FunctionPropertySpec {
entity_args: &[0],
property_name_args: &[(1, 0), (2, 0)],
needs_full_entity: false,
},
),
("KEYS", full_entity),
("PROPERTIES", full_entity),
("LABELS", full_entity),
("NODES", full_entity),
("RELATIONSHIPS", full_entity),
("COUNT", entity_arg_only),
("COALESCE", no_entity),
("SUM", no_entity),
("AVG", no_entity),
("MIN", no_entity),
("MAX", no_entity),
("COLLECT", no_entity),
("PERCENTILEDISC", no_entity),
("PERCENTILECONT", no_entity),
])
});
pub fn get_function_spec(name: &str) -> Option<&'static FunctionPropertySpec> {
let name_upper = name.to_uppercase();
FUNCTION_SPECS.get(name_upper.as_str())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validat_spec() {
let spec = get_function_spec("uni.temporal.validAt").unwrap();
assert_eq!(spec.entity_args, &[0]);
assert_eq!(spec.property_name_args, &[(1, 0), (2, 0)]);
assert!(!spec.needs_full_entity);
}
#[test]
fn test_keys_spec() {
let spec = get_function_spec("keys").unwrap();
assert_eq!(spec.entity_args, &[0]);
assert!(spec.needs_full_entity);
}
#[test]
fn test_properties_spec() {
let spec = get_function_spec("PROPERTIES").unwrap();
assert_eq!(spec.entity_args, &[0]);
assert!(spec.needs_full_entity);
}
#[test]
fn test_unknown_function_returns_none() {
assert!(get_function_spec("unknownFunction").is_none());
}
#[test]
fn test_count_spec_exists() {
let spec = get_function_spec("COUNT").unwrap();
assert!(!spec.needs_full_entity);
assert_eq!(spec.entity_args, &[0]);
}
#[test]
fn test_all_aggregates_registered() {
for func in ["COUNT", "SUM", "AVG", "MIN", "MAX", "COLLECT"] {
let spec = get_function_spec(func);
assert!(
spec.is_some(),
"Aggregate function {} should be registered",
func
);
assert!(
!spec.unwrap().needs_full_entity,
"Aggregate function {} should not need full entity",
func
);
}
}
#[test]
fn test_aggregate_case_insensitive() {
assert!(get_function_spec("count").is_some());
assert!(get_function_spec("Count").is_some());
assert!(get_function_spec("COUNT").is_some());
}
}