crux_core/effects/routes/parked.rs
1use std::sync::{Arc, Weak};
2
3use crate::{
4 Request, ResolveError,
5 capability::Operation,
6 effects::{EffectId, EffectRouter, Routes, registry::Registry},
7};
8
9/// A route for effects handled over a custom, user-owned FFI.
10///
11/// The "opaque typed" lane is for operations whose payloads or results are
12/// awkward or undesirable to serialize, such as pointer-style handles or large
13/// non-serializable buffers. Instead of bytes, the request is *parked* under an
14/// [`EffectId`] with [`Parked::park`], and the shell receives that id together
15/// with the (typed) operation. When the shell has a result, it calls
16/// [`Parked::resolve`] with the id and the typed output.
17///
18/// `Parked` keeps a [`Weak`] reference to its [`EffectRouter`] so that resolving
19/// a request can advance the runtime and route any follow-up effects.
20pub struct Parked<A, RouteSet, Op>
21where
22 A: crate::App,
23 RouteSet: Routes<A>,
24 Op: Operation,
25{
26 router: Weak<EffectRouter<A, RouteSet>>,
27 registry: Registry<Op>,
28}
29
30impl<App, RouteSet, Op> Parked<App, RouteSet, Op>
31where
32 App: crate::App,
33 RouteSet: Routes<App> + Send + Sync + 'static,
34 Op: Operation,
35{
36 /// Create a parked route attached to `router`.
37 ///
38 /// Called from your [`Routes::new`] implementation with the [`Weak`] router
39 /// handle the trait provides.
40 #[must_use]
41 pub fn new(router: Weak<EffectRouter<App, RouteSet>>) -> Self {
42 Self {
43 router,
44 registry: Registry::default(),
45 }
46 }
47
48 /// Resume a parked request and route any follow-up effects.
49 ///
50 /// # Errors
51 ///
52 /// Returns an error if the underlying request could not be resolved.
53 ///
54 /// # Panics
55 ///
56 /// Panics if the router has been dropped, or the internal registry lock has
57 /// been poisoned.
58 pub fn resolve(
59 &self,
60 id: EffectId<Op::Output>,
61 output: Op::Output,
62 ) -> Result<(), ResolveError> {
63 self.registry.resolve(id, output)?;
64 self.router().process();
65
66 Ok(())
67 }
68
69 fn router(&self) -> Arc<EffectRouter<App, RouteSet>> {
70 self.router.upgrade().expect("effect router dropped")
71 }
72}
73
74impl<App, RouteSet, Op> Parked<App, RouteSet, Op>
75where
76 App: crate::App,
77 RouteSet: Routes<App>,
78 Op: Operation,
79{
80 /// Park a request under an ID for a custom FFI to resume later.
81 ///
82 /// # Panics
83 ///
84 /// Panics if the internal registry lock has been poisoned.
85 #[must_use]
86 pub fn park(&self, request: Request<Op>) -> (EffectId<Op::Output>, Op) {
87 let (id, operation) = self.registry.register(request);
88
89 (id, operation)
90 }
91}