runmat_runtime/
user_functions.rs1use crate::RuntimeError;
2use runmat_builtins::Value;
3use runmat_hir::{CallableFallbackPolicy, CallableIdentity, SourceId};
4use runmat_thread_local::runmat_thread_local;
5use std::cell::RefCell;
6use std::future::Future;
7use std::pin::Pin;
8use std::sync::Arc;
9
10pub type UserFunctionFuture = Pin<Box<dyn Future<Output = Result<Value, RuntimeError>>>>;
11pub type DynamicFunctionLoadFuture =
12 Pin<Box<dyn Future<Output = Option<Result<Value, RuntimeError>>>>>;
13pub type FunctionInvoker = dyn Fn(usize, &[Value], usize) -> UserFunctionFuture + Send + Sync;
14pub type FunctionResolver = dyn Fn(&str) -> Option<usize> + Send + Sync;
15pub type DynamicFunctionLoader =
16 dyn Fn(String, Vec<Value>, usize) -> DynamicFunctionLoadFuture + Send + Sync;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct SourceFunctionInfo {
20 pub source_id: SourceId,
21 pub name: String,
22 pub function: usize,
23}
24
25#[derive(Debug, Clone)]
26pub struct CallableRequest {
27 identity: CallableIdentity,
28 fallback_policy: CallableFallbackPolicy,
29 args: Vec<Value>,
30 requested_outputs: usize,
31}
32
33impl CallableRequest {
34 pub fn semantic(function: usize, args: Vec<Value>, requested_outputs: usize) -> Self {
35 Self {
36 identity: CallableIdentity::BoundFunction(runmat_hir::FunctionId(function)),
37 fallback_policy: CallableFallbackPolicy::None,
38 args,
39 requested_outputs,
40 }
41 }
42
43 pub fn resolved(
44 identity: CallableIdentity,
45 fallback_policy: CallableFallbackPolicy,
46 args: Vec<Value>,
47 requested_outputs: usize,
48 ) -> Self {
49 Self {
50 identity,
51 fallback_policy,
52 args,
53 requested_outputs,
54 }
55 }
56}
57
58runmat_thread_local! {
59 static SEMANTIC_FUNCTION_INVOKER: RefCell<Option<Arc<FunctionInvoker>>> =
60 const { RefCell::new(None) };
61 static SEMANTIC_FUNCTION_RESOLVER: RefCell<Option<Arc<FunctionResolver>>> =
62 const { RefCell::new(None) };
63 static ACTIVE_RUNTIME_CONTEXT: RefCell<Option<Arc<RuntimeContext>>> =
64 const { RefCell::new(None) };
65 static SOURCE_FUNCTION_CATALOG: RefCell<Option<Arc<Vec<SourceFunctionInfo>>>> =
66 const { RefCell::new(None) };
67 static ACTIVE_SEMANTIC_FUNCTION_STACK: RefCell<Vec<usize>> =
68 const { RefCell::new(Vec::new()) };
69}
70
71pub struct FunctionInvokerGuard {
72 previous: Option<Arc<FunctionInvoker>>,
73}
74
75pub struct FunctionResolverGuard {
76 previous: Option<Arc<FunctionResolver>>,
77}
78
79pub struct RuntimeContextGuard {
80 previous: Option<Arc<RuntimeContext>>,
81}
82
83pub struct SourceFunctionCatalogGuard {
84 previous: Option<Arc<Vec<SourceFunctionInfo>>>,
85}
86
87pub struct ActiveSemanticFunctionGuard;
88
89impl Drop for FunctionInvokerGuard {
90 fn drop(&mut self) {
91 let previous = self.previous.take();
92 SEMANTIC_FUNCTION_INVOKER.with(|slot| {
93 *slot.borrow_mut() = previous;
94 });
95 }
96}
97
98impl Drop for FunctionResolverGuard {
99 fn drop(&mut self) {
100 let previous = self.previous.take();
101 SEMANTIC_FUNCTION_RESOLVER.with(|slot| {
102 *slot.borrow_mut() = previous;
103 });
104 }
105}
106
107impl Drop for RuntimeContextGuard {
108 fn drop(&mut self) {
109 let previous = self.previous.take();
110 ACTIVE_RUNTIME_CONTEXT.with(|slot| {
111 *slot.borrow_mut() = previous;
112 });
113 }
114}
115
116impl Drop for SourceFunctionCatalogGuard {
117 fn drop(&mut self) {
118 let previous = self.previous.take();
119 SOURCE_FUNCTION_CATALOG.with(|slot| {
120 *slot.borrow_mut() = previous;
121 });
122 }
123}
124
125impl Drop for ActiveSemanticFunctionGuard {
126 fn drop(&mut self) {
127 ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| {
128 slot.borrow_mut().pop();
129 });
130 }
131}
132
133pub fn install_semantic_function_invoker(
134 invoker: Option<Arc<FunctionInvoker>>,
135) -> FunctionInvokerGuard {
136 let previous =
137 SEMANTIC_FUNCTION_INVOKER.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), invoker));
138 FunctionInvokerGuard { previous }
139}
140
141pub fn install_semantic_function_resolver(
142 resolver: Option<Arc<FunctionResolver>>,
143) -> FunctionResolverGuard {
144 let previous = SEMANTIC_FUNCTION_RESOLVER
145 .with(|slot| std::mem::replace(&mut *slot.borrow_mut(), resolver));
146 FunctionResolverGuard { previous }
147}
148
149pub struct RuntimeContext {
150 search_path: Arc<crate::builtins::common::path_state::SearchPath>,
151 dynamic_function_loader: Option<Arc<DynamicFunctionLoader>>,
152}
153
154impl RuntimeContext {
155 pub fn new(search_path: Arc<crate::builtins::common::path_state::SearchPath>) -> Self {
156 Self {
157 search_path,
158 dynamic_function_loader: None,
159 }
160 }
161
162 pub fn with_dynamic_function_loader(mut self, loader: Arc<DynamicFunctionLoader>) -> Self {
163 self.dynamic_function_loader = Some(loader);
164 self
165 }
166
167 pub fn search_path(&self) -> &Arc<crate::builtins::common::path_state::SearchPath> {
168 &self.search_path
169 }
170}
171
172pub fn install_runtime_context(context: Arc<RuntimeContext>) -> RuntimeContextGuard {
173 let previous = ACTIVE_RUNTIME_CONTEXT.with(|slot| slot.borrow_mut().replace(context));
174 RuntimeContextGuard { previous }
175}
176
177pub fn active_runtime_context() -> Option<Arc<RuntimeContext>> {
178 ACTIVE_RUNTIME_CONTEXT.with(|slot| slot.borrow().clone())
179}
180
181pub fn install_source_function_catalog(
182 catalog: Option<Arc<Vec<SourceFunctionInfo>>>,
183) -> SourceFunctionCatalogGuard {
184 let previous =
185 SOURCE_FUNCTION_CATALOG.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), catalog));
186 SourceFunctionCatalogGuard { previous }
187}
188
189pub fn push_active_semantic_function(function: usize) -> ActiveSemanticFunctionGuard {
190 ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| {
191 slot.borrow_mut().push(function);
192 });
193 ActiveSemanticFunctionGuard
194}
195
196pub fn current_semantic_function_invoker() -> Option<Arc<FunctionInvoker>> {
197 SEMANTIC_FUNCTION_INVOKER.with(|slot| slot.borrow().clone())
198}
199
200pub fn current_semantic_function_resolver() -> Option<Arc<FunctionResolver>> {
201 SEMANTIC_FUNCTION_RESOLVER.with(|slot| slot.borrow().clone())
202}
203
204pub fn current_active_semantic_function() -> Option<usize> {
205 ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| slot.borrow().last().copied())
206}
207
208pub fn source_functions_for(source_id: SourceId) -> Vec<SourceFunctionInfo> {
209 SOURCE_FUNCTION_CATALOG.with(|slot| {
210 slot.borrow()
211 .as_ref()
212 .map(|catalog| {
213 catalog
214 .iter()
215 .filter(|info| info.source_id == source_id)
216 .cloned()
217 .collect::<Vec<_>>()
218 })
219 .unwrap_or_default()
220 })
221}
222
223pub async fn try_call_semantic_function(
224 function: usize,
225 args: &[Value],
226 requested_outputs: usize,
227) -> Option<Result<Value, RuntimeError>> {
228 let invoker = SEMANTIC_FUNCTION_INVOKER.with(|slot| slot.borrow().clone());
229 let invoker = invoker?;
230 Some(invoker(function, args, requested_outputs).await)
231}
232
233pub async fn try_call_semantic_function_by_name(
234 name: &str,
235 args: &[Value],
236 requested_outputs: usize,
237) -> Option<Result<Value, RuntimeError>> {
238 let function = resolve_semantic_function_by_name(name)?;
239 try_call_semantic_function(function, args, requested_outputs).await
240}
241
242pub fn resolve_semantic_function_by_name(name: &str) -> Option<usize> {
243 let resolver = SEMANTIC_FUNCTION_RESOLVER.with(|slot| slot.borrow().clone())?;
244 resolver(name)
245}
246
247pub async fn try_load_and_call_dynamic_function(
248 name: String,
249 args: Vec<Value>,
250 requested_outputs: usize,
251) -> Option<Result<Value, RuntimeError>> {
252 let loader = active_runtime_context()?.dynamic_function_loader.clone()?;
253 loader(name, args, requested_outputs).await
254}
255
256pub async fn try_call_semantic_descriptor(
257 request: CallableRequest,
258) -> Option<Result<Value, RuntimeError>> {
259 let CallableRequest {
260 identity,
261 fallback_policy,
262 args,
263 requested_outputs,
264 } = request;
265 if let CallableIdentity::BoundFunction(function) = identity {
266 return try_call_semantic_function(function.0, &args, requested_outputs).await;
267 }
268 if !fallback_policy.allows_semantic_name_resolution_for(&identity) {
269 return None;
270 }
271 let name = fallback_policy.resolution_name_for(&identity)?;
272 if matches!(identity, CallableIdentity::DynamicName(_))
273 && runmat_builtins::get_class(&name).is_some()
274 {
275 return None;
278 }
279 if let Some(result) = try_call_semantic_function_by_name(&name, &args, requested_outputs).await
280 {
281 return Some(result);
282 }
283 if matches!(
284 identity,
285 CallableIdentity::DynamicName(_)
286 | CallableIdentity::Imported(_)
287 | CallableIdentity::ExternalName(_)
288 ) {
289 return try_load_and_call_dynamic_function(name, args, requested_outputs).await;
290 }
291 None
292}