boxology_runtime/
local.rs1use crate::{TransportBinding, TransportHandle, TransportJoinFuture, TransportRuntime};
4use boxology_contract::{
5 CallContext, CapabilityDescriptor, CapabilityId, CapabilityShape, Detail, ErasedCallError,
6 ErasedCallTarget, ExposureLevel, SlotValue,
7};
8use std::{
9 future::Future,
10 pin::Pin,
11 sync::{Arc, Mutex, Weak},
12};
13
14#[derive(Default)]
16pub struct LocalBinding {
17 runtime: Mutex<Option<Weak<TransportRuntime<()>>>>,
18}
19
20impl LocalBinding {
21 pub fn new() -> Self {
23 Self::default()
24 }
25}
26
27#[doc(hidden)]
29pub struct LocalHandle(Arc<TransportRuntime<()>>);
30
31impl TransportHandle for LocalHandle {
32 fn stop_intake(&self) {}
33 fn cancel_tasks(&self) {}
34 fn abort_tasks(&self) {}
35 fn join_tasks(self: Box<Self>) -> TransportJoinFuture {
36 drop(self.0);
37 Box::pin(std::future::ready(Ok(())))
38 }
39}
40
41impl TransportBinding for LocalBinding {
42 type Config = ();
43 type Handle = LocalHandle;
44
45 fn config(&self) -> Arc<()> {
46 Arc::new(())
47 }
48
49 fn conform(
50 &self,
51 descriptor: &CapabilityDescriptor,
52 _level: ExposureLevel,
53 ) -> Result<(), Detail> {
54 matches!(descriptor.shape(), CapabilityShape::Unary)
55 .then_some(())
56 .ok_or_else(|| Detail::new("unsupported_interaction_shape"))
57 }
58
59 fn prepare(&self, _descriptors: &[&'static CapabilityDescriptor]) -> Result<(), Detail> {
60 Ok(())
61 }
62
63 fn start(&self, runtime: TransportRuntime<()>) -> Result<LocalHandle, Detail> {
64 let runtime = Arc::new(runtime);
65 let mut retained = self.runtime.lock().map_err(|_| Detail::new("local_lock"))?;
66 if retained.is_some() {
67 return Err(Detail::new("local_binding_already_started"));
68 }
69 *retained = Some(Arc::downgrade(&runtime));
70 Ok(LocalHandle(runtime))
71 }
72}
73
74impl ErasedCallTarget for LocalBinding {
75 fn call<'a>(
76 &'a self,
77 capability: &'a CapabilityId,
78 context: CallContext,
79 input: SlotValue,
80 ) -> Pin<Box<dyn Future<Output = Result<SlotValue, ErasedCallError>> + Send + 'a>> {
81 let exposure = self
82 .runtime
83 .lock()
84 .ok()
85 .and_then(|runtime| runtime.as_ref()?.upgrade())
86 .and_then(|runtime| {
87 runtime
88 .exposures()
89 .iter()
90 .find(|exposure| exposure.descriptor().id() == capability)
91 .cloned()
92 });
93 Box::pin(async move {
94 match exposure {
95 Some(exposure) => exposure.dispatch(context, input).await,
96 None => Err(ErasedCallError::Internal(Detail::new("local_capability"))),
97 }
98 })
99 }
100}