truecalc_core/eval/functions/
mod.rs1pub mod array;
2pub mod database;
3pub mod date;
4pub mod engineering;
5pub mod filter;
6pub mod financial;
7pub mod logical;
8pub mod lookup;
9pub mod math;
10pub mod operator;
11pub mod parser;
12pub mod statistical;
13pub mod text;
14pub mod web;
15
16use std::collections::HashMap;
17use crate::eval::context::Context;
18use crate::parser::ast::Expr;
19use crate::types::{ErrorKind, Value};
20
21pub struct EvalCtx<'r> {
26 pub ctx: Context,
27 pub registry: &'r Registry,
28}
29
30impl<'r> EvalCtx<'r> {
31 pub fn new(ctx: Context, registry: &'r Registry) -> Self {
32 Self { ctx, registry }
33 }
34}
35
36pub type EagerFn = fn(&[Value]) -> Value;
41
42pub type LazyFn = fn(&[Expr], &mut EvalCtx<'_>) -> Value;
45
46pub enum FunctionKind {
47 Eager(EagerFn),
48 Lazy(LazyFn),
49}
50
51#[derive(Debug)]
56pub struct FunctionMeta {
57 pub category: &'static str,
58 pub signature: &'static str,
59 pub description: &'static str,
60}
61
62pub struct Registry {
66 functions: HashMap<String, FunctionKind>,
67 metadata: HashMap<String, FunctionMeta>,
68}
69
70impl Registry {
71 pub fn new() -> Self {
72 let mut r = Self { functions: HashMap::new(), metadata: HashMap::new() };
73 math::register_math(&mut r);
74 logical::register_logical(&mut r);
75 text::register_text(&mut r);
76 financial::register_financial(&mut r);
77 statistical::register_statistical(&mut r);
78 operator::register_operator(&mut r);
79 date::register_date(&mut r);
80 parser::register_parser(&mut r);
81 engineering::register_engineering(&mut r);
82 filter::register_filter(&mut r);
83 array::register_array(&mut r);
84 database::register_database(&mut r);
85 lookup::register_lookup(&mut r);
86 web::register_web(&mut r);
87 r
88 }
89
90 pub fn register_eager(&mut self, name: &str, f: EagerFn, meta: FunctionMeta) {
93 let key = name.to_uppercase();
94 self.functions.insert(key.clone(), FunctionKind::Eager(f));
95 self.metadata.insert(key, meta);
96 }
97
98 pub fn register_lazy(&mut self, name: &str, f: LazyFn, meta: FunctionMeta) {
101 let key = name.to_uppercase();
102 self.functions.insert(key.clone(), FunctionKind::Lazy(f));
103 self.metadata.insert(key, meta);
104 }
105
106 pub fn register_internal(&mut self, name: &str, f: EagerFn) {
109 self.functions.insert(name.to_uppercase(), FunctionKind::Eager(f));
110 }
111
112 pub fn register_internal_lazy(&mut self, name: &str, f: LazyFn) {
115 self.functions.insert(name.to_uppercase(), FunctionKind::Lazy(f));
116 }
117
118 pub fn get(&self, name: &str) -> Option<&FunctionKind> {
119 self.functions.get(&name.to_uppercase())
120 }
121
122 pub fn list_functions(&self) -> impl Iterator<Item = (&str, &FunctionMeta)> {
125 self.metadata.iter().map(|(k, v)| (k.as_str(), v))
126 }
127}
128
129impl Default for Registry {
130 fn default() -> Self {
131 Self::new()
132 }
133}
134
135pub fn check_arity(args: &[Value], min: usize, max: usize) -> Option<Value> {
139 if args.len() < min || args.len() > max {
140 Some(Value::Error(ErrorKind::NA))
141 } else {
142 None
143 }
144}
145
146pub fn check_arity_len(count: usize, min: usize, max: usize) -> Option<Value> {
149 if count < min || count > max {
150 Some(Value::Error(ErrorKind::NA))
151 } else {
152 None
153 }
154}
155
156#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn list_functions_matches_registry() {
164 let registry = Registry::new();
165 let listed: Vec<(&str, &FunctionMeta)> = registry.list_functions().collect();
166 assert!(!listed.is_empty(), "registry should expose at least one function");
167 for (name, _meta) in &listed {
169 assert!(
170 registry.get(name).is_some(),
171 "listed function {name} not found via get()"
172 );
173 }
174 assert_eq!(listed.len(), registry.metadata.len());
176 }
177}