runmat_runtime/
user_functions.rs1use crate::RuntimeError;
2use runmat_builtins::Value;
3use runmat_thread_local::runmat_thread_local;
4use std::cell::RefCell;
5use std::future::Future;
6use std::pin::Pin;
7use std::sync::Arc;
8
9pub type UserFunctionFuture = Pin<Box<dyn Future<Output = Result<Value, RuntimeError>>>>;
10pub type UserFunctionInvoker = dyn Fn(&str, &[Value]) -> UserFunctionFuture + Send + Sync;
11
12runmat_thread_local! {
13 static USER_FUNCTION_INVOKER: RefCell<Option<Arc<UserFunctionInvoker>>> =
14 const { RefCell::new(None) };
15}
16
17pub struct UserFunctionInvokerGuard {
18 previous: Option<Arc<UserFunctionInvoker>>,
19}
20
21impl Drop for UserFunctionInvokerGuard {
22 fn drop(&mut self) {
23 let previous = self.previous.take();
24 USER_FUNCTION_INVOKER.with(|slot| {
25 *slot.borrow_mut() = previous;
26 });
27 }
28}
29
30pub fn install_user_function_invoker(
31 invoker: Option<Arc<UserFunctionInvoker>>,
32) -> UserFunctionInvokerGuard {
33 let previous =
34 USER_FUNCTION_INVOKER.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), invoker));
35 UserFunctionInvokerGuard { previous }
36}
37
38pub async fn try_call_user_function(
39 name: &str,
40 args: &[Value],
41) -> Option<Result<Value, RuntimeError>> {
42 let invoker = USER_FUNCTION_INVOKER.with(|slot| slot.borrow().clone());
43 let invoker = invoker?;
44 let result = invoker(name, args).await;
45 match result {
46 Err(err)
47 if err
48 .identifier()
49 .map(|id| id.ends_with("UndefinedFunction"))
50 .unwrap_or(false)
51 && err
52 .message()
53 .contains(&format!("Undefined function: {name}")) =>
54 {
55 None
56 }
57 other => Some(other),
58 }
59}