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 FunctionInvoker = dyn Fn(usize, &[Value], usize) -> UserFunctionFuture + Send + Sync;
12pub type FunctionResolver = dyn Fn(&str) -> Option<usize> + Send + Sync;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct SourceFunctionInfo {
16 pub source_id: SourceId,
17 pub name: String,
18 pub function: usize,
19}
20
21#[derive(Debug, Clone)]
22pub struct CallableRequest {
23 identity: CallableIdentity,
24 fallback_policy: CallableFallbackPolicy,
25 args: Vec<Value>,
26 requested_outputs: usize,
27}
28
29impl CallableRequest {
30 pub fn semantic(function: usize, args: Vec<Value>, requested_outputs: usize) -> Self {
31 Self {
32 identity: CallableIdentity::BoundFunction(runmat_hir::FunctionId(function)),
33 fallback_policy: CallableFallbackPolicy::None,
34 args,
35 requested_outputs,
36 }
37 }
38
39 pub fn resolved(
40 identity: CallableIdentity,
41 fallback_policy: CallableFallbackPolicy,
42 args: Vec<Value>,
43 requested_outputs: usize,
44 ) -> Self {
45 Self {
46 identity,
47 fallback_policy,
48 args,
49 requested_outputs,
50 }
51 }
52}
53
54runmat_thread_local! {
55 static SEMANTIC_FUNCTION_INVOKER: RefCell<Option<Arc<FunctionInvoker>>> =
56 const { RefCell::new(None) };
57 static SEMANTIC_FUNCTION_RESOLVER: RefCell<Option<Arc<FunctionResolver>>> =
58 const { RefCell::new(None) };
59 static SOURCE_FUNCTION_CATALOG: RefCell<Option<Arc<Vec<SourceFunctionInfo>>>> =
60 const { RefCell::new(None) };
61 static ACTIVE_SEMANTIC_FUNCTION_STACK: RefCell<Vec<usize>> =
62 const { RefCell::new(Vec::new()) };
63}
64
65pub struct FunctionInvokerGuard {
66 previous: Option<Arc<FunctionInvoker>>,
67}
68
69pub struct FunctionResolverGuard {
70 previous: Option<Arc<FunctionResolver>>,
71}
72
73pub struct SourceFunctionCatalogGuard {
74 previous: Option<Arc<Vec<SourceFunctionInfo>>>,
75}
76
77pub struct ActiveSemanticFunctionGuard;
78
79impl Drop for FunctionInvokerGuard {
80 fn drop(&mut self) {
81 let previous = self.previous.take();
82 SEMANTIC_FUNCTION_INVOKER.with(|slot| {
83 *slot.borrow_mut() = previous;
84 });
85 }
86}
87
88impl Drop for FunctionResolverGuard {
89 fn drop(&mut self) {
90 let previous = self.previous.take();
91 SEMANTIC_FUNCTION_RESOLVER.with(|slot| {
92 *slot.borrow_mut() = previous;
93 });
94 }
95}
96
97impl Drop for SourceFunctionCatalogGuard {
98 fn drop(&mut self) {
99 let previous = self.previous.take();
100 SOURCE_FUNCTION_CATALOG.with(|slot| {
101 *slot.borrow_mut() = previous;
102 });
103 }
104}
105
106impl Drop for ActiveSemanticFunctionGuard {
107 fn drop(&mut self) {
108 ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| {
109 slot.borrow_mut().pop();
110 });
111 }
112}
113
114pub fn install_semantic_function_invoker(
115 invoker: Option<Arc<FunctionInvoker>>,
116) -> FunctionInvokerGuard {
117 let previous =
118 SEMANTIC_FUNCTION_INVOKER.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), invoker));
119 FunctionInvokerGuard { previous }
120}
121
122pub fn install_semantic_function_resolver(
123 resolver: Option<Arc<FunctionResolver>>,
124) -> FunctionResolverGuard {
125 let previous = SEMANTIC_FUNCTION_RESOLVER
126 .with(|slot| std::mem::replace(&mut *slot.borrow_mut(), resolver));
127 FunctionResolverGuard { previous }
128}
129
130pub fn install_source_function_catalog(
131 catalog: Option<Arc<Vec<SourceFunctionInfo>>>,
132) -> SourceFunctionCatalogGuard {
133 let previous =
134 SOURCE_FUNCTION_CATALOG.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), catalog));
135 SourceFunctionCatalogGuard { previous }
136}
137
138pub fn push_active_semantic_function(function: usize) -> ActiveSemanticFunctionGuard {
139 ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| {
140 slot.borrow_mut().push(function);
141 });
142 ActiveSemanticFunctionGuard
143}
144
145pub fn current_semantic_function_invoker() -> Option<Arc<FunctionInvoker>> {
146 SEMANTIC_FUNCTION_INVOKER.with(|slot| slot.borrow().clone())
147}
148
149pub fn current_semantic_function_resolver() -> Option<Arc<FunctionResolver>> {
150 SEMANTIC_FUNCTION_RESOLVER.with(|slot| slot.borrow().clone())
151}
152
153pub fn current_active_semantic_function() -> Option<usize> {
154 ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| slot.borrow().last().copied())
155}
156
157pub fn source_functions_for(source_id: SourceId) -> Vec<SourceFunctionInfo> {
158 SOURCE_FUNCTION_CATALOG.with(|slot| {
159 slot.borrow()
160 .as_ref()
161 .map(|catalog| {
162 catalog
163 .iter()
164 .filter(|info| info.source_id == source_id)
165 .cloned()
166 .collect::<Vec<_>>()
167 })
168 .unwrap_or_default()
169 })
170}
171
172pub async fn try_call_semantic_function(
173 function: usize,
174 args: &[Value],
175 requested_outputs: usize,
176) -> Option<Result<Value, RuntimeError>> {
177 let invoker = SEMANTIC_FUNCTION_INVOKER.with(|slot| slot.borrow().clone());
178 let invoker = invoker?;
179 Some(invoker(function, args, requested_outputs).await)
180}
181
182pub async fn try_call_semantic_function_by_name(
183 name: &str,
184 args: &[Value],
185 requested_outputs: usize,
186) -> Option<Result<Value, RuntimeError>> {
187 let function = resolve_semantic_function_by_name(name)?;
188 try_call_semantic_function(function, args, requested_outputs).await
189}
190
191pub fn resolve_semantic_function_by_name(name: &str) -> Option<usize> {
192 let resolver = SEMANTIC_FUNCTION_RESOLVER.with(|slot| slot.borrow().clone())?;
193 resolver(name)
194}
195
196pub async fn try_call_semantic_descriptor(
197 request: CallableRequest,
198) -> Option<Result<Value, RuntimeError>> {
199 let CallableRequest {
200 identity,
201 fallback_policy,
202 args,
203 requested_outputs,
204 } = request;
205 if let CallableIdentity::BoundFunction(function) = identity {
206 return try_call_semantic_function(function.0, &args, requested_outputs).await;
207 }
208 if !fallback_policy.allows_semantic_name_resolution_for(&identity) {
209 return None;
210 }
211 let name = fallback_policy.resolution_name_for(&identity)?;
212 if matches!(identity, CallableIdentity::DynamicName(_))
213 && runmat_builtins::get_class(&name).is_some()
214 {
215 return None;
218 }
219 try_call_semantic_function_by_name(&name, &args, requested_outputs).await
220}