crux_core/bridge/
request_serde.rs

1use crate::{
2    capability::Operation,
3    core::{Resolve, ResolveError},
4    Request,
5};
6
7use super::BridgeError;
8
9// used in docs/internals/bridge.md
10// ANCHOR: resolve_serialized
11type ResolveOnceSerialized =
12    Box<dyn FnOnce(&mut dyn erased_serde::Deserializer) -> Result<(), BridgeError> + Send>;
13type ResolveManySerialized =
14    Box<dyn FnMut(&mut dyn erased_serde::Deserializer) -> Result<(), BridgeError> + Send>;
15
16/// A deserializing version of Resolve
17///
18/// ResolveSerialized is a separate type because lifetime elision doesn't work
19/// through generic type arguments. We can't create a ResolveRegistry of
20/// Resolve<&[u8]> without specifying an explicit lifetime.
21/// If you see a better way around this, please open a PR.
22pub enum ResolveSerialized {
23    Never,
24    Once(ResolveOnceSerialized),
25    Many(ResolveManySerialized),
26}
27// ANCHOR_END: resolve_serialized
28
29impl ResolveSerialized {
30    pub(crate) fn resolve(
31        &mut self,
32        bytes: &mut dyn erased_serde::Deserializer,
33    ) -> Result<(), BridgeError> {
34        match self {
35            ResolveSerialized::Never => Err(BridgeError::ProcessResponse(ResolveError::Never)),
36            ResolveSerialized::Many(f) => f(bytes),
37            ResolveSerialized::Once(_) => {
38                // The resolve has been used, turn it into a Never
39                let ResolveSerialized::Once(f) = std::mem::replace(self, ResolveSerialized::Never)
40                else {
41                    unreachable!();
42                };
43
44                f(bytes)
45            }
46        }
47    }
48}
49
50impl<Op> Request<Op>
51where
52    Op: Operation,
53{
54    /// Serialize this effect request using `effect` as a constructor
55    /// for a serializable Effect `Eff`
56    ///
57    /// You should never need to call this method yourself, it will be called
58    /// by the generated implementation of [`Effect::serialize`](crate::Effect::serialize),
59    /// which is used by the Bridge implementation.
60    pub fn serialize<F, Eff>(self, effect: F) -> (Eff, ResolveSerialized)
61    where
62        F: FnOnce(Op) -> Eff,
63    {
64        // FIXME should Eff be bound as `Serializable`?
65        let (operation, resolve) = (self.operation, self.resolve);
66
67        let resolve = resolve.deserializing(move |deserializer| {
68            erased_serde::deserialize(deserializer).map_err(BridgeError::DeserializeOutput)
69        });
70
71        (effect(operation), resolve)
72    }
73}
74
75impl<Out> Resolve<Out> {
76    /// Convert this Resolve into a version which deserializes from bytes, consuming it.
77    /// The `func` argument is a 'deserializer' converting from bytes into the `Out` type.
78    fn deserializing<F>(self, mut func: F) -> ResolveSerialized
79    where
80        F: (FnMut(&mut dyn erased_serde::Deserializer) -> Result<Out, BridgeError>)
81            + Send
82            + Sync
83            + 'static,
84        Out: 'static,
85    {
86        match self {
87            Resolve::Never => ResolveSerialized::Never,
88            Resolve::Once(resolve) => ResolveSerialized::Once(Box::new(move |deser| {
89                let out = func(deser)?;
90                resolve(out);
91                Ok(())
92            })),
93            Resolve::Many(resolve) => ResolveSerialized::Many(Box::new(move |deser| {
94                let out = func(deser)?;
95                resolve(out).map_err(|_| BridgeError::ProcessResponse(ResolveError::FinishedMany))
96            })),
97        }
98    }
99}