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
46#[derive(Clone)]
47pub enum FunctionKind {
48 Eager(EagerFn),
49 Lazy(LazyFn),
50}
51
52#[derive(Debug, Clone)]
57pub struct FunctionMeta {
58 pub category: &'static str,
59 pub signature: &'static str,
60 pub description: &'static str,
61}
62
63pub struct FunctionMetaEntry<'a> {
65 pub name: &'a str,
66 pub meta: &'a FunctionMeta,
67}
68
69pub struct Registry {
73 pub functions: HashMap<String, FunctionKind>,
74 pub metadata: HashMap<String, FunctionMeta>,
75}
76
77impl Registry {
78 pub fn new() -> Self {
79 let mut r = Self { functions: HashMap::new(), metadata: HashMap::new() };
80 math::register_math(&mut r);
81 logical::register_logical(&mut r);
82 text::register_text(&mut r);
83 financial::register_financial(&mut r);
84 statistical::register_statistical(&mut r);
85 operator::register_operator(&mut r);
86 date::register_date(&mut r);
87 parser::register_parser(&mut r);
88 engineering::register_engineering(&mut r);
89 filter::register_filter(&mut r);
90 array::register_array(&mut r);
91 database::register_database(&mut r);
92 lookup::register_lookup(&mut r);
93 web::register_web(&mut r);
94 r
95 }
96
97 pub fn register_eager(&mut self, name: &str, f: EagerFn, meta: FunctionMeta) {
101 let key = name.to_uppercase();
102 assert!(
103 !self.functions.contains_key(&key),
104 "duplicate function registration: '{}'",
105 key
106 );
107 self.functions.insert(key.clone(), FunctionKind::Eager(f));
108 self.metadata.insert(key, meta);
109 }
110
111 pub fn register_lazy(&mut self, name: &str, f: LazyFn, meta: FunctionMeta) {
115 let key = name.to_uppercase();
116 assert!(
117 !self.functions.contains_key(&key),
118 "duplicate function registration: '{}'",
119 key
120 );
121 self.functions.insert(key.clone(), FunctionKind::Lazy(f));
122 self.metadata.insert(key, meta);
123 }
124
125 pub fn register_alias(&mut self, alias: &str, canonical: &str) {
130 let alias_key = alias.to_uppercase();
131 let canonical_key = canonical.to_uppercase();
132 assert!(
133 !self.functions.contains_key(&alias_key),
134 "duplicate function registration: '{}'",
135 alias_key
136 );
137 let kind = self
138 .functions
139 .get(&canonical_key)
140 .unwrap_or_else(|| {
141 panic!(
142 "register_alias: canonical '{}' must be registered before alias '{}'",
143 canonical_key, alias_key
144 )
145 })
146 .clone();
147 self.functions.insert(alias_key, kind);
148 }
150
151 pub fn register_internal(&mut self, name: &str, f: EagerFn) {
154 self.functions.insert(name.to_uppercase(), FunctionKind::Eager(f));
155 }
156
157 pub fn register_internal_lazy(&mut self, name: &str, f: LazyFn) {
160 self.functions.insert(name.to_uppercase(), FunctionKind::Lazy(f));
161 }
162
163 pub fn get(&self, name: &str) -> Option<&FunctionKind> {
164 self.functions.get(&name.to_uppercase())
165 }
166
167 pub fn list_functions(&self) -> impl Iterator<Item = (&str, &FunctionMeta)> {
170 self.metadata.iter().map(|(k, v)| (k.as_str(), v))
171 }
172
173 pub fn get_metadata(&self) -> Vec<FunctionMetaEntry<'_>> {
176 self.metadata
177 .iter()
178 .map(|(k, v)| FunctionMetaEntry { name: k.as_str(), meta: v })
179 .collect()
180 }
181
182 pub fn metadata_names(&self) -> Vec<String> {
184 self.metadata.keys().cloned().collect()
185 }
186}
187
188impl Registry {
189 pub const VOLATILE_FUNCTIONS: &'static [&'static str] = &[
192 "RAND", "RANDARRAY", "NOW", "TODAY", "RANDBETWEEN",
193 ];
194}
195
196impl Default for Registry {
197 fn default() -> Self {
198 Self::new()
199 }
200}
201
202pub fn check_arity(args: &[Value], min: usize, max: usize) -> Option<Value> {
206 if args.len() < min || args.len() > max {
207 Some(Value::Error(ErrorKind::NA))
208 } else {
209 None
210 }
211}
212
213pub fn check_arity_len(count: usize, min: usize, max: usize) -> Option<Value> {
216 if count < min || count > max {
217 Some(Value::Error(ErrorKind::NA))
218 } else {
219 None
220 }
221}
222
223#[cfg(test)]
226mod tests {
227 use super::*;
228
229 #[test]
230 fn list_functions_matches_registry() {
231 let registry = Registry::new();
232 let listed: Vec<(&str, &FunctionMeta)> = registry.list_functions().collect();
233 assert!(!listed.is_empty(), "registry should expose at least one function");
234 for (name, _meta) in &listed {
236 assert!(
237 registry.get(name).is_some(),
238 "listed function {name} not found via get()"
239 );
240 }
241 assert_eq!(listed.len(), registry.metadata.len());
243 }
244}