pub mod array;
pub mod database;
pub mod date;
pub mod engineering;
pub mod filter;
pub mod financial;
pub mod logical;
pub mod lookup;
pub mod math;
pub mod operator;
pub mod parser;
pub mod statistical;
pub mod text;
pub mod web;
use std::collections::HashMap;
use crate::eval::context::Context;
use crate::parser::ast::Expr;
use crate::types::{ErrorKind, Value};
pub struct EvalCtx<'r> {
pub ctx: Context,
pub registry: &'r Registry,
}
impl<'r> EvalCtx<'r> {
pub fn new(ctx: Context, registry: &'r Registry) -> Self {
Self { ctx, registry }
}
}
pub type EagerFn = fn(&[Value]) -> Value;
pub type LazyFn = fn(&[Expr], &mut EvalCtx<'_>) -> Value;
#[derive(Clone)]
pub enum FunctionKind {
Eager(EagerFn),
Lazy(LazyFn),
}
#[derive(Debug, Clone)]
pub struct FunctionMeta {
pub category: &'static str,
pub signature: &'static str,
pub description: &'static str,
}
pub struct FunctionMetaEntry<'a> {
pub name: &'a str,
pub meta: &'a FunctionMeta,
}
pub struct Registry {
pub functions: HashMap<String, FunctionKind>,
pub metadata: HashMap<String, FunctionMeta>,
}
impl Registry {
pub fn new() -> Self {
let mut r = Self { functions: HashMap::new(), metadata: HashMap::new() };
math::register_math(&mut r);
logical::register_logical(&mut r);
text::register_text(&mut r);
financial::register_financial(&mut r);
statistical::register_statistical(&mut r);
operator::register_operator(&mut r);
date::register_date(&mut r);
parser::register_parser(&mut r);
engineering::register_engineering(&mut r);
filter::register_filter(&mut r);
array::register_array(&mut r);
database::register_database(&mut r);
lookup::register_lookup(&mut r);
web::register_web(&mut r);
r
}
pub fn register_eager(&mut self, name: &str, f: EagerFn, meta: FunctionMeta) {
let key = name.to_uppercase();
assert!(
!self.functions.contains_key(&key),
"duplicate function registration: '{}'",
key
);
self.functions.insert(key.clone(), FunctionKind::Eager(f));
self.metadata.insert(key, meta);
}
pub fn register_lazy(&mut self, name: &str, f: LazyFn, meta: FunctionMeta) {
let key = name.to_uppercase();
assert!(
!self.functions.contains_key(&key),
"duplicate function registration: '{}'",
key
);
self.functions.insert(key.clone(), FunctionKind::Lazy(f));
self.metadata.insert(key, meta);
}
pub fn register_alias(&mut self, alias: &str, canonical: &str) {
let alias_key = alias.to_uppercase();
let canonical_key = canonical.to_uppercase();
assert!(
!self.functions.contains_key(&alias_key),
"duplicate function registration: '{}'",
alias_key
);
let kind = self
.functions
.get(&canonical_key)
.unwrap_or_else(|| {
panic!(
"register_alias: canonical '{}' must be registered before alias '{}'",
canonical_key, alias_key
)
})
.clone();
self.functions.insert(alias_key, kind);
}
pub fn register_internal(&mut self, name: &str, f: EagerFn) {
self.functions.insert(name.to_uppercase(), FunctionKind::Eager(f));
}
pub fn register_internal_lazy(&mut self, name: &str, f: LazyFn) {
self.functions.insert(name.to_uppercase(), FunctionKind::Lazy(f));
}
pub fn get(&self, name: &str) -> Option<&FunctionKind> {
self.functions.get(&name.to_uppercase())
}
pub fn list_functions(&self) -> impl Iterator<Item = (&str, &FunctionMeta)> {
self.metadata.iter().map(|(k, v)| (k.as_str(), v))
}
pub fn get_metadata(&self) -> Vec<FunctionMetaEntry<'_>> {
self.metadata
.iter()
.map(|(k, v)| FunctionMetaEntry { name: k.as_str(), meta: v })
.collect()
}
pub fn metadata_names(&self) -> Vec<String> {
self.metadata.keys().cloned().collect()
}
}
impl Registry {
pub const VOLATILE_FUNCTIONS: &'static [&'static str] = &[
"RAND", "RANDARRAY", "NOW", "TODAY", "RANDBETWEEN",
];
}
impl Default for Registry {
fn default() -> Self {
Self::new()
}
}
pub fn check_arity(args: &[Value], min: usize, max: usize) -> Option<Value> {
if args.len() < min || args.len() > max {
Some(Value::Error(ErrorKind::NA))
} else {
None
}
}
pub fn check_arity_len(count: usize, min: usize, max: usize) -> Option<Value> {
if count < min || count > max {
Some(Value::Error(ErrorKind::NA))
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn list_functions_matches_registry() {
let registry = Registry::new();
let listed: Vec<(&str, &FunctionMeta)> = registry.list_functions().collect();
assert!(!listed.is_empty(), "registry should expose at least one function");
for (name, _meta) in &listed {
assert!(
registry.get(name).is_some(),
"listed function {name} not found via get()"
);
}
assert_eq!(listed.len(), registry.metadata.len());
}
}