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::eval::resolver::Resolver;
19use crate::parser::ast::Expr;
20use crate::types::{ErrorKind, Value};
21
22pub struct EvalCtx<'r> {
34 pub ctx: Context,
35 pub registry: &'r Registry,
36 pub resolver: Option<&'r mut dyn Resolver>,
40}
41
42impl<'r> EvalCtx<'r> {
43 pub fn new(ctx: Context, registry: &'r Registry) -> Self {
47 Self { ctx, registry, resolver: None }
48 }
49
50 pub fn with_resolver(
52 ctx: Context,
53 registry: &'r Registry,
54 resolver: &'r mut dyn Resolver,
55 ) -> Self {
56 Self { ctx, registry, resolver: Some(resolver) }
57 }
58
59 pub fn resolve_ref(&mut self, r: &crate::parser::refs::Ref) -> Value {
63 match self.resolver {
64 Some(ref mut res) => res.resolve(r),
65 None => Value::Empty,
66 }
67 }
68}
69
70pub type EagerFn = fn(&[Value]) -> Value;
75
76pub type LazyFn = fn(&[Expr], &mut EvalCtx<'_>) -> Value;
79
80#[derive(Clone)]
81pub enum FunctionKind {
82 Eager(EagerFn),
83 Lazy(LazyFn),
84}
85
86#[derive(Debug, Clone)]
91pub struct FunctionMeta {
92 pub category: &'static str,
93 pub signature: &'static str,
94 pub description: &'static str,
95}
96
97pub struct FunctionMetaEntry<'a> {
99 pub name: &'a str,
100 pub meta: &'a FunctionMeta,
101}
102
103pub struct Registry {
107 pub functions: HashMap<String, FunctionKind>,
108 pub metadata: HashMap<String, FunctionMeta>,
109}
110
111impl Registry {
112 pub fn new() -> Self {
113 let mut r = Self { functions: HashMap::new(), metadata: HashMap::new() };
114 math::register_math(&mut r);
115 logical::register_logical(&mut r);
116 text::register_text(&mut r);
117 financial::register_financial(&mut r);
118 statistical::register_statistical(&mut r);
119 operator::register_operator(&mut r);
120 date::register_date(&mut r);
121 parser::register_parser(&mut r);
122 engineering::register_engineering(&mut r);
123 filter::register_filter(&mut r);
124 array::register_array(&mut r);
125 database::register_database(&mut r);
126 lookup::register_lookup(&mut r);
127 web::register_web(&mut r);
128 r
129 }
130
131 pub fn register_eager(&mut self, name: &str, f: EagerFn, meta: FunctionMeta) {
135 let key = name.to_uppercase();
136 assert!(
137 !self.functions.contains_key(&key),
138 "duplicate function registration: '{}'",
139 key
140 );
141 self.functions.insert(key.clone(), FunctionKind::Eager(f));
142 self.metadata.insert(key, meta);
143 }
144
145 pub fn register_lazy(&mut self, name: &str, f: LazyFn, meta: FunctionMeta) {
149 let key = name.to_uppercase();
150 assert!(
151 !self.functions.contains_key(&key),
152 "duplicate function registration: '{}'",
153 key
154 );
155 self.functions.insert(key.clone(), FunctionKind::Lazy(f));
156 self.metadata.insert(key, meta);
157 }
158
159 pub fn register_alias(&mut self, alias: &str, canonical: &str) {
164 let alias_key = alias.to_uppercase();
165 let canonical_key = canonical.to_uppercase();
166 assert!(
167 !self.functions.contains_key(&alias_key),
168 "duplicate function registration: '{}'",
169 alias_key
170 );
171 let kind = self
172 .functions
173 .get(&canonical_key)
174 .unwrap_or_else(|| {
175 panic!(
176 "register_alias: canonical '{}' must be registered before alias '{}'",
177 canonical_key, alias_key
178 )
179 })
180 .clone();
181 self.functions.insert(alias_key, kind);
182 }
184
185 pub fn register_internal(&mut self, name: &str, f: EagerFn) {
188 self.functions.insert(name.to_uppercase(), FunctionKind::Eager(f));
189 }
190
191 pub fn register_internal_lazy(&mut self, name: &str, f: LazyFn) {
194 self.functions.insert(name.to_uppercase(), FunctionKind::Lazy(f));
195 }
196
197 pub fn get(&self, name: &str) -> Option<&FunctionKind> {
198 self.functions.get(&name.to_uppercase())
199 }
200
201 pub fn list_functions(&self) -> impl Iterator<Item = (&str, &FunctionMeta)> {
204 self.metadata.iter().map(|(k, v)| (k.as_str(), v))
205 }
206
207 pub fn get_metadata(&self) -> Vec<FunctionMetaEntry<'_>> {
210 self.metadata
211 .iter()
212 .map(|(k, v)| FunctionMetaEntry { name: k.as_str(), meta: v })
213 .collect()
214 }
215
216 pub fn metadata_names(&self) -> Vec<String> {
218 self.metadata.keys().cloned().collect()
219 }
220}
221
222impl Registry {
223 pub const VOLATILE_FUNCTIONS: &'static [&'static str] = &[
226 "RAND", "RANDARRAY", "NOW", "TODAY", "RANDBETWEEN",
227 ];
228}
229
230impl Default for Registry {
231 fn default() -> Self {
232 Self::new()
233 }
234}
235
236pub fn check_arity(args: &[Value], min: usize, max: usize) -> Option<Value> {
240 if args.len() < min || args.len() > max {
241 Some(Value::Error(ErrorKind::NA))
242 } else {
243 None
244 }
245}
246
247pub fn check_arity_len(count: usize, min: usize, max: usize) -> Option<Value> {
250 if count < min || count > max {
251 Some(Value::Error(ErrorKind::NA))
252 } else {
253 None
254 }
255}
256
257#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn list_functions_matches_registry() {
265 let registry = Registry::new();
266 let listed: Vec<(&str, &FunctionMeta)> = registry.list_functions().collect();
267 assert!(!listed.is_empty(), "registry should expose at least one function");
268 for (name, _meta) in &listed {
270 assert!(
271 registry.get(name).is_some(),
272 "listed function {name} not found via get()"
273 );
274 }
275 assert_eq!(listed.len(), registry.metadata.len());
277 }
278}