ganit_core/eval/functions/
mod.rs1pub mod date;
2pub mod financial;
3pub mod logical;
4pub mod math;
5pub mod operator;
6pub mod statistical;
7pub mod text;
8
9use std::collections::HashMap;
10use crate::eval::context::Context;
11use crate::parser::ast::Expr;
12use crate::types::{ErrorKind, Value};
13
14pub struct EvalCtx<'r> {
19 pub ctx: Context,
20 pub registry: &'r Registry,
21}
22
23impl<'r> EvalCtx<'r> {
24 pub fn new(ctx: Context, registry: &'r Registry) -> Self {
25 Self { ctx, registry }
26 }
27}
28
29pub type EagerFn = fn(&[Value]) -> Value;
34
35pub type LazyFn = fn(&[Expr], &mut EvalCtx<'_>) -> Value;
38
39pub enum FunctionKind {
40 Eager(EagerFn),
41 Lazy(LazyFn),
42}
43
44#[derive(Debug)]
49pub struct FunctionMeta {
50 pub category: &'static str,
51 pub signature: &'static str,
52 pub description: &'static str,
53}
54
55pub struct Registry {
59 functions: HashMap<String, FunctionKind>,
60 metadata: HashMap<String, FunctionMeta>,
61}
62
63impl Registry {
64 pub fn new() -> Self {
65 let mut r = Self { functions: HashMap::new(), metadata: HashMap::new() };
66 math::register_math(&mut r);
67 logical::register_logical(&mut r);
68 text::register_text(&mut r);
69 financial::register_financial(&mut r);
70 statistical::register_statistical(&mut r);
71 operator::register_operator(&mut r);
72 date::register_date(&mut r);
73 r
74 }
75
76 pub fn register_eager(&mut self, name: &str, f: EagerFn, meta: FunctionMeta) {
79 let key = name.to_uppercase();
80 self.functions.insert(key.clone(), FunctionKind::Eager(f));
81 self.metadata.insert(key, meta);
82 }
83
84 pub fn register_lazy(&mut self, name: &str, f: LazyFn, meta: FunctionMeta) {
87 let key = name.to_uppercase();
88 self.functions.insert(key.clone(), FunctionKind::Lazy(f));
89 self.metadata.insert(key, meta);
90 }
91
92 pub fn register_internal(&mut self, name: &str, f: EagerFn) {
95 self.functions.insert(name.to_uppercase(), FunctionKind::Eager(f));
96 }
97
98 pub fn register_internal_lazy(&mut self, name: &str, f: LazyFn) {
101 self.functions.insert(name.to_uppercase(), FunctionKind::Lazy(f));
102 }
103
104 pub fn get(&self, name: &str) -> Option<&FunctionKind> {
105 self.functions.get(&name.to_uppercase())
106 }
107
108 pub fn list_functions(&self) -> impl Iterator<Item = (&str, &FunctionMeta)> {
111 self.metadata.iter().map(|(k, v)| (k.as_str(), v))
112 }
113}
114
115impl Default for Registry {
116 fn default() -> Self {
117 Self::new()
118 }
119}
120
121pub fn check_arity(args: &[Value], min: usize, max: usize) -> Option<Value> {
125 if args.len() < min || args.len() > max {
126 Some(Value::Error(ErrorKind::NA))
127 } else {
128 None
129 }
130}
131
132pub fn check_arity_len(count: usize, min: usize, max: usize) -> Option<Value> {
135 if count < min || count > max {
136 Some(Value::Error(ErrorKind::NA))
137 } else {
138 None
139 }
140}
141
142#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn list_functions_matches_registry() {
150 let registry = Registry::new();
151 let listed: Vec<(&str, &FunctionMeta)> = registry.list_functions().collect();
152 assert!(!listed.is_empty(), "registry should expose at least one function");
153 for (name, _meta) in &listed {
155 assert!(
156 registry.get(name).is_some(),
157 "listed function {name} not found via get()"
158 );
159 }
160 assert_eq!(listed.len(), registry.metadata.len());
162 }
163}