Skip to main content

runmat_runtime/
user_functions.rs

1use 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}