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