1use super::{RuntimeCapability, RuntimeCapabilityError};
2use crate::builtin::RuntimeBuiltinBinding;
3use crate::class_registry::RuntimeClass;
4use crate::warning_store::RuntimeWarning;
5use crate::RuntimeError;
6use runmat_types::{CallableIdentity, SourceId};
7use runmat_value::Value;
8use std::future::Future;
9use std::pin::Pin;
10use std::rc::Rc;
11
12pub type RuntimeServiceFuture<T> = Pin<Box<dyn Future<Output = T> + 'static>>;
13
14#[derive(Debug, Clone)]
15pub struct RuntimeCallRequest {
16 pub identity: CallableIdentity,
17 pub arguments: Vec<Value>,
18 pub requested_outputs: usize,
19}
20
21pub trait RuntimeCallService {
22 fn resolve(&self, name: &str) -> Option<usize>;
23
24 fn invoke(
25 &self,
26 request: RuntimeCallRequest,
27 ) -> RuntimeServiceFuture<Result<Value, RuntimeError>>;
28
29 fn source_functions(&self, _source_id: SourceId) -> Vec<(String, usize)> {
30 Vec::new()
31 }
32}
33
34pub trait RuntimeBuiltinService {
38 fn bindings_by_name(&self, name: &str) -> Vec<RuntimeBuiltinBinding>;
39}
40
41pub trait RuntimeWorkspaceService {
42 fn lookup(&self, name: &str) -> Option<Value>;
43 fn snapshot(&self) -> Vec<(String, Value)>;
44 fn global_names(&self) -> Vec<String>;
45 fn assign(&self, name: &str, value: Value) -> Result<(), RuntimeError>;
46 fn clear(&self) -> Result<(), RuntimeError>;
47 fn remove(&self, name: &str) -> Result<(), RuntimeError>;
48}
49
50pub trait RuntimeObjectService {
51 fn class(&self, name: &str) -> Option<RuntimeClass>;
52 fn register_class(&self, class: RuntimeClass) -> Result<(), RuntimeError>;
53 fn static_property(&self, class: &str, property: &str) -> Option<Value>;
54 fn set_static_property(
55 &self,
56 class: &str,
57 property: &str,
58 value: Value,
59 ) -> Result<(), RuntimeError>;
60}
61
62#[derive(Debug, Clone, PartialEq, Eq)]
63pub enum HostInteraction {
64 Line { prompt: String, echo: bool },
65 KeyPress { prompt: String },
66}
67
68pub trait RuntimeHostService {
69 fn console(&self, stream: crate::console::ConsoleStream, text: String);
70
71 fn interact(
72 &self,
73 request: HostInteraction,
74 ) -> RuntimeServiceFuture<Result<crate::interaction::InteractionResponse, RuntimeError>>;
75
76 fn warning(&self, warning: RuntimeWarning);
77}
78
79pub trait RuntimeErrorService {
80 fn report(&self, error: &RuntimeError);
81}
82
83pub trait RuntimeAccelerationService {
84 fn supports_operation(&self, operation: &str) -> bool;
85}
86
87pub trait RuntimePlacementService {
91 fn plan(
92 &self,
93 request: runmat_execution::PlacementPlanRequest,
94 ) -> Result<runmat_execution::PlacementDecision, RuntimeError>;
95
96 fn observe(&self, feedback: runmat_execution::PlacementFeedback) -> Result<(), RuntimeError>;
97
98 fn invalidate(&self, invalidation: runmat_execution::PlacementInvalidation);
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102pub enum NativeCapability {
103 SharedLibrary,
104 ExecutableMemory,
105 ObjectEmission,
106}
107
108pub trait RuntimeNativeService {
109 fn supports(&self, capability: NativeCapability) -> bool;
110}
111
112#[derive(Debug, Clone)]
113pub struct ForeignCall {
114 pub adapter: String,
115 pub symbol: String,
116 pub arguments: Vec<Value>,
117 pub requested_outputs: usize,
118}
119
120pub trait RuntimeForeignService {
121 fn invoke(&self, call: ForeignCall) -> RuntimeServiceFuture<Result<Value, RuntimeError>>;
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub enum ParallelCapability {
126 Pool,
127 Parfor,
128 Spmd,
129 DistributedValues,
130 Collectives,
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub struct RuntimeParallelResources {
135 pub cpu_millicores_available: u32,
136 pub memory_available_bytes: Option<u64>,
137 pub epoch: u64,
138}
139
140impl Default for RuntimeParallelResources {
141 fn default() -> Self {
142 Self {
143 cpu_millicores_available: 1_000,
144 memory_available_bytes: None,
145 epoch: 0,
146 }
147 }
148}
149
150pub trait RuntimeParallelService {
151 fn supports(&self, capability: ParallelCapability) -> bool;
152
153 fn placement_resources(&self) -> RuntimeParallelResources {
157 RuntimeParallelResources::default()
158 }
159}
160
161#[derive(Clone, Default)]
165pub struct RuntimeServicePorts {
166 call: Option<Rc<dyn RuntimeCallService>>,
167 builtin: Option<Rc<dyn RuntimeBuiltinService>>,
168 workspace: Option<Rc<dyn RuntimeWorkspaceService>>,
169 object: Option<Rc<dyn RuntimeObjectService>>,
170 host: Option<Rc<dyn RuntimeHostService>>,
171 error: Option<Rc<dyn RuntimeErrorService>>,
172 acceleration: Option<Rc<dyn RuntimeAccelerationService>>,
173 placement: Option<Rc<dyn RuntimePlacementService>>,
174 native: Option<Rc<dyn RuntimeNativeService>>,
175 foreign: Option<Rc<dyn RuntimeForeignService>>,
176 parallel: Option<Rc<dyn RuntimeParallelService>>,
177}
178
179impl std::fmt::Debug for RuntimeServicePorts {
180 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181 formatter
182 .debug_struct("RuntimeServicePorts")
183 .field("call", &self.call.is_some())
184 .field("builtin", &self.builtin.is_some())
185 .field("workspace", &self.workspace.is_some())
186 .field("object", &self.object.is_some())
187 .field("host", &self.host.is_some())
188 .field("error", &self.error.is_some())
189 .field("acceleration", &self.acceleration.is_some())
190 .field("placement", &self.placement.is_some())
191 .field("native", &self.native.is_some())
192 .field("foreign", &self.foreign.is_some())
193 .field("parallel", &self.parallel.is_some())
194 .finish()
195 }
196}
197
198macro_rules! port_accessors {
199 ($with:ident, $get:ident, $require:ident, $field:ident, $trait_name:ident, $cap:ident) => {
200 pub fn $with(mut self, service: Rc<dyn $trait_name>) -> Self {
201 self.$field = Some(service);
202 self
203 }
204
205 pub fn $get(&self) -> Option<&Rc<dyn $trait_name>> {
206 self.$field.as_ref()
207 }
208
209 pub fn $require(
210 &self,
211 operation: impl Into<String>,
212 ) -> Result<&Rc<dyn $trait_name>, RuntimeCapabilityError> {
213 self.$field
214 .as_ref()
215 .ok_or_else(|| RuntimeCapabilityError::new(RuntimeCapability::$cap, operation))
216 }
217 };
218}
219
220impl RuntimeServicePorts {
221 port_accessors!(
222 with_call,
223 call,
224 require_call,
225 call,
226 RuntimeCallService,
227 Call
228 );
229 port_accessors!(
230 with_builtin,
231 builtin,
232 require_builtin,
233 builtin,
234 RuntimeBuiltinService,
235 Builtin
236 );
237 port_accessors!(
238 with_workspace,
239 workspace,
240 require_workspace,
241 workspace,
242 RuntimeWorkspaceService,
243 Workspace
244 );
245
246 pub fn without_workspace(mut self) -> Self {
250 self.workspace = None;
251 self
252 }
253 port_accessors!(
254 with_object,
255 object,
256 require_object,
257 object,
258 RuntimeObjectService,
259 Object
260 );
261 port_accessors!(
262 with_host,
263 host,
264 require_host,
265 host,
266 RuntimeHostService,
267 Host
268 );
269 port_accessors!(
270 with_error,
271 error,
272 require_error,
273 error,
274 RuntimeErrorService,
275 Error
276 );
277 port_accessors!(
278 with_acceleration,
279 acceleration,
280 require_acceleration,
281 acceleration,
282 RuntimeAccelerationService,
283 Acceleration
284 );
285 port_accessors!(
286 with_placement,
287 placement,
288 require_placement,
289 placement,
290 RuntimePlacementService,
291 Placement
292 );
293 port_accessors!(
294 with_native,
295 native,
296 require_native,
297 native,
298 RuntimeNativeService,
299 Native
300 );
301 port_accessors!(
302 with_foreign,
303 foreign,
304 require_foreign,
305 foreign,
306 RuntimeForeignService,
307 Foreign
308 );
309 port_accessors!(
310 with_parallel,
311 parallel,
312 require_parallel,
313 parallel,
314 RuntimeParallelService,
315 Parallel
316 );
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322
323 #[test]
324 fn absent_ports_report_stable_typed_capabilities() {
325 fn missing<T>(result: Result<T, RuntimeCapabilityError>) -> RuntimeCapabilityError {
326 match result {
327 Ok(_) => panic!("expected absent runtime port"),
328 Err(error) => error,
329 }
330 }
331 let ports = RuntimeServicePorts::default();
332 let failures = [
333 missing(ports.require_builtin("resolve builtin")),
334 missing(ports.require_call("invoke")),
335 missing(ports.require_workspace("lookup")),
336 missing(ports.require_object("class lookup")),
337 missing(ports.require_host("console write")),
338 missing(ports.require_error("report error")),
339 missing(ports.require_acceleration("resident operation")),
340 missing(ports.require_placement("placement plan")),
341 missing(ports.require_native("load library")),
342 missing(ports.require_foreign("foreign call")),
343 missing(ports.require_parallel("parfor")),
344 ];
345 assert_eq!(
346 failures
347 .iter()
348 .map(|failure| failure.capability)
349 .collect::<Vec<_>>(),
350 vec![
351 RuntimeCapability::Builtin,
352 RuntimeCapability::Call,
353 RuntimeCapability::Workspace,
354 RuntimeCapability::Object,
355 RuntimeCapability::Host,
356 RuntimeCapability::Error,
357 RuntimeCapability::Acceleration,
358 RuntimeCapability::Placement,
359 RuntimeCapability::Native,
360 RuntimeCapability::Foreign,
361 RuntimeCapability::Parallel,
362 ]
363 );
364 assert!(failures.iter().all(|failure| {
365 failure.clone().into_runtime_error().identifier()
366 == Some(RuntimeCapabilityError::IDENTIFIER)
367 }));
368 }
369}