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
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::function::FnCaps;
105
106 struct TestFn {
107 ns: &'static str,
108 name: &'static str,
109 aliases: &'static [&'static str],
110 }
111
112 impl Function for TestFn {
113 fn caps(&self) -> FnCaps {
114 FnCaps::PURE
115 }
116
117 fn name(&self) -> &'static str {
118 self.name
119 }
120
121 fn namespace(&self) -> &'static str {
122 self.ns
123 }
124
125 fn aliases(&self) -> &'static [&'static str] {
126 self.aliases
127 }
128
129 fn eval<'a, 'b, 'c>(
130 &self,
131 _args: &'c [crate::traits::ArgumentHandle<'a, 'b>],
132 _ctx: &dyn crate::traits::FunctionContext<'b>,
133 ) -> Result<crate::traits::CalcValue<'b>, formualizer_common::ExcelError> {
134 Ok(crate::traits::CalcValue::Scalar(
135 formualizer_common::LiteralValue::Number(1.0),
136 ))
137 }
138 }
139
140 #[test]
141 fn resolves_single_excel_prefix() {
142 let ns = "__REG_PREFIX_SINGLE__";
143 register_function(Arc::new(TestFn {
144 ns,
145 name: "SUM",
146 aliases: &[],
147 }));
148
149 let f = get(ns, "_xlfn.sum").expect("function should resolve");
150 assert_eq!(f.name(), "SUM");
151 }
152
153 #[test]
154 fn resolves_chained_excel_prefixes() {
155 let ns = "__REG_PREFIX_CHAINED__";
156 register_function(Arc::new(TestFn {
157 ns,
158 name: "FILTER",
159 aliases: &[],
160 }));
161
162 let f = get(ns, "_xlfn._xlws.filter").expect("function should resolve");
163 assert_eq!(f.name(), "FILTER");
164 }
165
166 #[test]
167 fn resolves_chained_prefixes_with_alias_target() {
168 let ns = "__REG_PREFIX_ALIAS__";
169 register_function(Arc::new(TestFn {
170 ns,
171 name: "MODERN",
172 aliases: &["LEGACY"],
173 }));
174
175 let f = get(ns, "_xlfn._xlws.legacy").expect("function should resolve");
176 assert_eq!(f.name(), "MODERN");
177 }
178
179 #[test]
180 fn direct_prefixed_registration_wins_before_compat_stripping() {
181 let ns = "__REG_DIRECT_PREFIX__";
182 register_function(Arc::new(TestFn {
183 ns,
184 name: "SUM",
185 aliases: &[],
186 }));
187 register_function(Arc::new(TestFn {
188 ns,
189 name: "_XLFN.SUM",
190 aliases: &[],
191 }));
192
193 let f = get(ns, "_xlfn.sum").expect("function should resolve");
194 assert_eq!(f.name(), "_XLFN.SUM");
195 }
196}