Skip to main content

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}