formualizer_eval/
function_registry.rs1use crate::function::Function;
2use dashmap::DashMap;
3use once_cell::sync::Lazy;
4use std::sync::Arc;
5
6static REG: Lazy<DashMap<(String, String), Arc<dyn Function>>> = Lazy::new(DashMap::new);
8
9static ALIASES: Lazy<DashMap<(String, String), (String, String)>> = Lazy::new(DashMap::new);
11
12#[inline]
13fn norm<S: AsRef<str>>(s: S) -> String {
14 s.as_ref().to_uppercase()
15}
16
17pub fn register_function(f: Arc<dyn Function>) {
18 let ns = norm(f.namespace());
19 let name = norm(f.name());
20 let key = (ns.clone(), name.clone());
21 REG.insert(key.clone(), Arc::clone(&f));
23 for &alias in f.aliases() {
25 if alias.eq_ignore_ascii_case(&name) {
26 continue;
27 }
28 let akey = (ns.clone(), norm(alias));
29 ALIASES.insert(akey, key.clone());
30 }
31}
32
33const EXCEL_PREFIXES: &[&str] = &["_XLFN.", "_XLL.", "_XLWS."];
35
36fn resolve_registered(key: &(String, String)) -> Option<Arc<dyn Function>> {
37 if let Some(v) = REG.get(key) {
39 return Some(Arc::clone(v.value()));
40 }
41
42 if let Some(canon) = ALIASES.get(key)
44 && let Some(v) = REG.get(canon.value())
45 {
46 return Some(Arc::clone(v.value()));
47 }
48
49 None
50}
51
52pub fn get(ns: &str, name: &str) -> Option<Arc<dyn Function>> {
53 let ns_norm = norm(ns);
54 let normalized_name = norm(name);
55 let key = (ns_norm.clone(), normalized_name.clone());
56
57 if let Some(v) = resolve_registered(&key) {
58 return Some(v);
59 }
60
61 let mut candidate = normalized_name.as_str();
68 loop {
69 let mut stripped_any = false;
70 for prefix in EXCEL_PREFIXES {
71 if let Some(rest) = candidate.strip_prefix(prefix) {
72 candidate = rest;
73 stripped_any = true;
74
75 let stripped_key = (ns_norm.clone(), candidate.to_string());
76 if let Some(v) = resolve_registered(&stripped_key) {
77 ALIASES.insert(key.clone(), stripped_key);
79 return Some(v);
80 }
81
82 break;
83 }
84 }
85
86 if !stripped_any {
87 break;
88 }
89 }
90
91 None
92}
93
94pub fn register_alias(ns: &str, alias: &str, target_ns: &str, target_name: &str) {
96 let akey = (norm(ns), norm(alias));
97 let tkey = (norm(target_ns), norm(target_name));
98 ALIASES.insert(akey, tkey);
99}
100
101pub fn snapshot_registered() -> Vec<(String, String, Arc<dyn Function>)> {
105 REG.iter()
106 .map(|entry| {
107 let ((ns, name), func) = entry.pair();
108 (ns.clone(), name.clone(), Arc::clone(func))
109 })
110 .collect()
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use crate::function::FnCaps;
117
118 struct TestFn {
119 ns: &'static str,
120 name: &'static str,
121 aliases: &'static [&'static str],
122 }
123
124 impl Function for TestFn {
125 fn caps(&self) -> FnCaps {
126 FnCaps::PURE
127 }
128
129 fn name(&self) -> &'static str {
130 self.name
131 }
132
133 fn namespace(&self) -> &'static str {
134 self.ns
135 }
136
137 fn aliases(&self) -> &'static [&'static str] {
138 self.aliases
139 }
140
141 fn eval<'a, 'b, 'c>(
142 &self,
143 _args: &'c [crate::traits::ArgumentHandle<'a, 'b>],
144 _ctx: &dyn crate::traits::FunctionContext<'b>,
145 ) -> Result<crate::traits::CalcValue<'b>, formualizer_common::ExcelError> {
146 Ok(crate::traits::CalcValue::Scalar(
147 formualizer_common::LiteralValue::Number(1.0),
148 ))
149 }
150 }
151
152 #[test]
153 fn resolves_single_excel_prefix() {
154 let ns = "__REG_PREFIX_SINGLE__";
155 register_function(Arc::new(TestFn {
156 ns,
157 name: "SUM",
158 aliases: &[],
159 }));
160
161 let f = get(ns, "_xlfn.sum").expect("function should resolve");
162 assert_eq!(f.name(), "SUM");
163 }
164
165 #[test]
166 fn resolves_chained_excel_prefixes() {
167 let ns = "__REG_PREFIX_CHAINED__";
168 register_function(Arc::new(TestFn {
169 ns,
170 name: "FILTER",
171 aliases: &[],
172 }));
173
174 let f = get(ns, "_xlfn._xlws.filter").expect("function should resolve");
175 assert_eq!(f.name(), "FILTER");
176 }
177
178 #[test]
179 fn resolves_chained_prefixes_with_alias_target() {
180 let ns = "__REG_PREFIX_ALIAS__";
181 register_function(Arc::new(TestFn {
182 ns,
183 name: "MODERN",
184 aliases: &["LEGACY"],
185 }));
186
187 let f = get(ns, "_xlfn._xlws.legacy").expect("function should resolve");
188 assert_eq!(f.name(), "MODERN");
189 }
190
191 #[test]
192 fn direct_prefixed_registration_wins_before_compat_stripping() {
193 let ns = "__REG_DIRECT_PREFIX__";
194 register_function(Arc::new(TestFn {
195 ns,
196 name: "SUM",
197 aliases: &[],
198 }));
199 register_function(Arc::new(TestFn {
200 ns,
201 name: "_XLFN.SUM",
202 aliases: &[],
203 }));
204
205 let f = get(ns, "_xlfn.sum").expect("function should resolve");
206 assert_eq!(f.name(), "_XLFN.SUM");
207 }
208}