Skip to main content

ganit_core/eval/functions/
mod.rs

1pub mod date;
2pub mod engineering;
3pub mod financial;
4pub mod logical;
5pub mod math;
6pub mod operator;
7pub mod parser;
8pub mod statistical;
9pub mod text;
10
11use std::collections::HashMap;
12use crate::eval::context::Context;
13use crate::parser::ast::Expr;
14use crate::types::{ErrorKind, Value};
15
16// ── EvalCtx ───────────────────────────────────────────────────────────────
17
18/// Bundles the variable context and function registry for use during evaluation.
19/// Passed to lazy functions so they can recursively evaluate sub-expressions.
20pub struct EvalCtx<'r> {
21    pub ctx: Context,
22    pub registry: &'r Registry,
23}
24
25impl<'r> EvalCtx<'r> {
26    pub fn new(ctx: Context, registry: &'r Registry) -> Self {
27        Self { ctx, registry }
28    }
29}
30
31// ── Function kinds ─────────────────────────────────────────────────────────
32
33/// A function that receives pre-evaluated arguments.
34/// Argument errors are caught before dispatch — the slice never contains `Value::Error`.
35pub type EagerFn = fn(&[Value]) -> Value;
36
37/// A function that receives raw AST nodes and controls its own evaluation order.
38/// Used for short-circuit operators like `IF`, `AND`, `OR`.
39pub type LazyFn  = fn(&[Expr], &mut EvalCtx<'_>) -> Value;
40
41pub enum FunctionKind {
42    Eager(EagerFn),
43    Lazy(LazyFn),
44}
45
46// ── FunctionMeta ──────────────────────────────────────────────────────────
47
48/// Metadata for a user-facing spreadsheet function.
49/// Co-located with the registration call so it can never drift.
50#[derive(Debug)]
51pub struct FunctionMeta {
52    pub category: &'static str,
53    pub signature: &'static str,
54    pub description: &'static str,
55}
56
57// ── Registry ──────────────────────────────────────────────────────────────
58
59/// The runtime registry of built-in and user-registered spreadsheet functions.
60pub struct Registry {
61    functions: HashMap<String, FunctionKind>,
62    metadata: HashMap<String, FunctionMeta>,
63}
64
65impl Registry {
66    pub fn new() -> Self {
67        let mut r = Self { functions: HashMap::new(), metadata: HashMap::new() };
68        math::register_math(&mut r);
69        logical::register_logical(&mut r);
70        text::register_text(&mut r);
71        financial::register_financial(&mut r);
72        statistical::register_statistical(&mut r);
73        operator::register_operator(&mut r);
74        date::register_date(&mut r);
75        parser::register_parser(&mut r);
76        engineering::register_engineering(&mut r);
77        r
78    }
79
80    /// Register a user-facing eager function with metadata.
81    /// Appears in `list_functions()`.
82    pub fn register_eager(&mut self, name: &str, f: EagerFn, meta: FunctionMeta) {
83        let key = name.to_uppercase();
84        self.functions.insert(key.clone(), FunctionKind::Eager(f));
85        self.metadata.insert(key, meta);
86    }
87
88    /// Register a user-facing lazy function with metadata.
89    /// Appears in `list_functions()`.
90    pub fn register_lazy(&mut self, name: &str, f: LazyFn, meta: FunctionMeta) {
91        let key = name.to_uppercase();
92        self.functions.insert(key.clone(), FunctionKind::Lazy(f));
93        self.metadata.insert(key, meta);
94    }
95
96    /// Register an internal/compiler-only eager function without metadata.
97    /// Never appears in `list_functions()`.
98    pub fn register_internal(&mut self, name: &str, f: EagerFn) {
99        self.functions.insert(name.to_uppercase(), FunctionKind::Eager(f));
100    }
101
102    /// Register an internal/compiler-only lazy function without metadata.
103    /// Never appears in `list_functions()`.
104    pub fn register_internal_lazy(&mut self, name: &str, f: LazyFn) {
105        self.functions.insert(name.to_uppercase(), FunctionKind::Lazy(f));
106    }
107
108    pub fn get(&self, name: &str) -> Option<&FunctionKind> {
109        self.functions.get(&name.to_uppercase())
110    }
111
112    /// Iterate all user-facing functions with their metadata.
113    /// The registry is the single source of truth — this can never drift.
114    pub fn list_functions(&self) -> impl Iterator<Item = (&str, &FunctionMeta)> {
115        self.metadata.iter().map(|(k, v)| (k.as_str(), v))
116    }
117}
118
119impl Default for Registry {
120    fn default() -> Self {
121        Self::new()
122    }
123}
124
125/// Validate argument count for eager functions (args already evaluated to `&[Value]`).
126/// Returns `Some(Value::Error(ErrorKind::NA))` if the count is out of range
127/// (matches Google Sheets / Excel behaviour for wrong argument count).
128pub fn check_arity(args: &[Value], min: usize, max: usize) -> Option<Value> {
129    if args.len() < min || args.len() > max {
130        Some(Value::Error(ErrorKind::NA))
131    } else {
132        None
133    }
134}
135
136/// Validate argument count for lazy functions (args are `&[Expr]`).
137/// Returns `Some(Value::Error(ErrorKind::NA))` if the count is out of range.
138pub fn check_arity_len(count: usize, min: usize, max: usize) -> Option<Value> {
139    if count < min || count > max {
140        Some(Value::Error(ErrorKind::NA))
141    } else {
142        None
143    }
144}
145
146// ── Tests ─────────────────────────────────────────────────────────────────
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151
152    #[test]
153    fn list_functions_matches_registry() {
154        let registry = Registry::new();
155        let listed: Vec<(&str, &FunctionMeta)> = registry.list_functions().collect();
156        assert!(!listed.is_empty(), "registry should expose at least one function");
157        // Every listed name must be resolvable — catches metadata/functions map skew
158        for (name, _meta) in &listed {
159            assert!(
160                registry.get(name).is_some(),
161                "listed function {name} not found via get()"
162            );
163        }
164        // metadata count == listed count (no orphaned metadata entries)
165        assert_eq!(listed.len(), registry.metadata.len());
166    }
167}