use crate::RuntimeError;
use runmat_builtins::Value;
use runmat_thread_local::runmat_thread_local;
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
pub type UserFunctionFuture = Pin<Box<dyn Future<Output = Result<Value, RuntimeError>>>>;
pub type UserFunctionInvoker = dyn Fn(&str, &[Value]) -> UserFunctionFuture + Send + Sync;
runmat_thread_local! {
static USER_FUNCTION_INVOKER: RefCell<Option<Arc<UserFunctionInvoker>>> =
const { RefCell::new(None) };
}
pub struct UserFunctionInvokerGuard {
previous: Option<Arc<UserFunctionInvoker>>,
}
impl Drop for UserFunctionInvokerGuard {
fn drop(&mut self) {
let previous = self.previous.take();
USER_FUNCTION_INVOKER.with(|slot| {
*slot.borrow_mut() = previous;
});
}
}
pub fn install_user_function_invoker(
invoker: Option<Arc<UserFunctionInvoker>>,
) -> UserFunctionInvokerGuard {
let previous =
USER_FUNCTION_INVOKER.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), invoker));
UserFunctionInvokerGuard { previous }
}
pub async fn try_call_user_function(
name: &str,
args: &[Value],
) -> Option<Result<Value, RuntimeError>> {
let invoker = USER_FUNCTION_INVOKER.with(|slot| slot.borrow().clone());
let invoker = invoker?;
let result = invoker(name, args).await;
match result {
Err(err)
if err
.identifier()
.map(|id| id.ends_with("UndefinedFunction"))
.unwrap_or(false)
&& err
.message()
.contains(&format!("Undefined function: {name}")) =>
{
None
}
other => Some(other),
}
}