crux_core/bridge/
mod.rs

1mod registry;
2mod request_serde;
3
4use bincode::{DefaultOptions, Options};
5use erased_serde::{Error as SerdeError, Serialize as _};
6use serde::{Deserialize, Serialize};
7use thiserror::Error;
8
9use crate::{core::ResolveError, App, Core};
10use registry::{EffectId, ResolveRegistry};
11// ResolveByte is public to be accessible from crux_macros
12#[doc(hidden)]
13pub use request_serde::ResolveSerialized;
14
15/// Request for a side-effect passed from the Core to the Shell. The `EffectId` links
16/// the `Request` with the corresponding call to [`Core::resolve`] to pass the data back
17/// to the [`App::update`] function (wrapped in the event provided to the capability originating the effect).
18// used in docs/internals/bridge.md
19// ANCHOR: request
20#[derive(Debug, Serialize, Deserialize)]
21pub struct Request<Eff>
22where
23    Eff: Serialize,
24{
25    pub id: EffectId,
26    pub effect: Eff,
27}
28// ANCHOR_END: request
29
30/// Bridge is a core wrapper presenting the same interface as the [`Core`] but in a
31/// serialized form, using bincode as the serialization format.
32pub struct Bridge<A>
33where
34    A: App,
35{
36    inner: BridgeWithSerializer<A>,
37}
38
39#[derive(Debug, Error)]
40pub enum BridgeError {
41    #[error("could not deserialize event: {0}")]
42    DeserializeEvent(SerdeError),
43    #[error("could not deserialize provided effect output: {0}")]
44    DeserializeOutput(SerdeError),
45    #[error("could not process response: {0}")]
46    ProcessResponse(#[from] ResolveError),
47    #[error("could not serialize effect requests: {0}")]
48    SerializeRequests(SerdeError),
49    #[error("could not serialize view model: {0}")]
50    SerializeView(SerdeError),
51}
52
53impl<A> Bridge<A>
54where
55    A: App,
56{
57    /// Create a new Bridge using the provided `core`.
58    pub fn new(core: Core<A>) -> Self {
59        Self {
60            inner: BridgeWithSerializer::new(core),
61        }
62    }
63
64    /// Receive an event from the shell.
65    ///
66    /// The `event` is serialized and will be deserialized by the core before it's passed
67    /// to your app.
68    pub fn process_event(&self, event: &[u8]) -> Result<Vec<u8>, BridgeError>
69    where
70        A::Event: for<'a> Deserialize<'a>,
71    {
72        let options = Self::bincode_options();
73
74        let mut deser = bincode::Deserializer::from_slice(event, options);
75
76        let mut return_buffer = vec![];
77        let mut ser = bincode::Serializer::new(&mut return_buffer, options);
78
79        self.inner.process_event(&mut deser, &mut ser)?;
80
81        Ok(return_buffer)
82    }
83
84    /// Receive a response to a capability request from the shell.
85    ///
86    /// The `output` is serialized capability output. It will be deserialized by the core.
87    /// The `id` MUST match the `id` of the effect that triggered it, else the core will panic.
88    // used in docs/internals/bridge.md
89    // ANCHOR: handle_response_sig
90    pub fn handle_response(&self, id: u32, output: &[u8]) -> Result<Vec<u8>, BridgeError>
91    // ANCHOR_END: handle_response_sig
92    where
93        A::Event: for<'a> Deserialize<'a>,
94    {
95        let options = Self::bincode_options();
96
97        let mut deser = bincode::Deserializer::from_slice(output, options);
98
99        let mut return_buffer = vec![];
100        let mut ser = bincode::Serializer::new(&mut return_buffer, options);
101
102        self.inner.handle_response(id, &mut deser, &mut ser)?;
103
104        Ok(return_buffer)
105    }
106
107    /// Get the current state of the app's view model (serialized).
108    pub fn view(&self) -> Result<Vec<u8>, BridgeError> {
109        let options = Self::bincode_options();
110
111        let mut return_buffer = vec![];
112
113        self.inner
114            .view(&mut bincode::Serializer::new(&mut return_buffer, options))?;
115
116        Ok(return_buffer)
117    }
118
119    fn bincode_options() -> impl bincode::Options + Copy {
120        DefaultOptions::new()
121            .with_fixint_encoding()
122            .allow_trailing_bytes()
123    }
124}
125
126/// A bridge with a user supplied serializer
127///
128/// This is exactly the same as [`Bridge`], except instead of using the default
129/// bincode serialization, you can provide your own [`Serializer`](::serde::ser::Serializer).
130///
131/// **Warning**: the support for custom serialization is **experimental** and
132/// does not have a corresponding type generation support - you will need
133/// to write deserialization code on the shell side yourself, or generate
134/// it using separate tooling.
135// used in docs/internals/bridge.md
136// ANCHOR: bridge_with_serializer
137pub struct BridgeWithSerializer<A>
138where
139    A: App,
140{
141    core: Core<A>,
142    registry: ResolveRegistry,
143}
144// ANCHOR_END: bridge_with_serializer
145
146impl<A> BridgeWithSerializer<A>
147where
148    A: App,
149{
150    pub fn new(core: Core<A>) -> Self {
151        Self {
152            core,
153            registry: Default::default(),
154        }
155    }
156
157    /// Receive an event from the shell.
158    ///
159    /// The `event` is serialized and will be deserialized by the core before it's passed
160    /// to your app.
161    pub fn process_event<'de, D, S>(&self, event: D, requests_out: S) -> Result<(), BridgeError>
162    where
163        for<'a> A::Event: Deserialize<'a>,
164        D: ::serde::de::Deserializer<'de> + 'de,
165        S: ::serde::ser::Serializer,
166    {
167        let mut erased_de = <dyn erased_serde::Deserializer>::erase(event);
168        self.process(
169            None,
170            &mut erased_de,
171            &mut <dyn erased_serde::Serializer>::erase(requests_out),
172        )
173    }
174
175    /// Receive a response to a capability request from the shell.
176    ///
177    /// The `output` is serialized capability output. It will be deserialized by the core.
178    /// The `id` MUST match the `id` of the effect that triggered it, else the core will panic.
179    pub fn handle_response<'de, D, S>(
180        &self,
181        id: u32,
182        response: D,
183        requests_out: S,
184    ) -> Result<(), BridgeError>
185    where
186        for<'a> A::Event: Deserialize<'a>,
187        D: ::serde::de::Deserializer<'de>,
188        S: ::serde::ser::Serializer,
189    {
190        let mut erased_response = <dyn erased_serde::Deserializer>::erase(response);
191        self.process(
192            Some(EffectId(id)),
193            &mut erased_response,
194            &mut <dyn erased_serde::Serializer>::erase(requests_out),
195        )
196    }
197
198    fn process(
199        &self,
200        id: Option<EffectId>,
201        data: &mut dyn erased_serde::Deserializer,
202        requests_out: &mut dyn erased_serde::Serializer,
203    ) -> Result<(), BridgeError>
204    where
205        A::Event: for<'a> Deserialize<'a>,
206    {
207        let effects = match id {
208            None => {
209                let shell_event =
210                    erased_serde::deserialize(data).map_err(BridgeError::DeserializeEvent)?;
211
212                self.core.process_event(shell_event)
213            }
214            Some(id) => {
215                self.registry.resume(id, data)?;
216
217                self.core.process()
218            }
219        };
220
221        let requests: Vec<_> = effects
222            .into_iter()
223            .map(|eff| self.registry.register(eff))
224            .collect();
225
226        requests
227            .erased_serialize(requests_out)
228            .map_err(BridgeError::SerializeRequests)?;
229
230        Ok(())
231    }
232
233    /// Get the current state of the app's view model (serialized).
234    pub fn view<S>(&self, ser: S) -> Result<(), BridgeError>
235    where
236        S: ::serde::ser::Serializer,
237    {
238        self.core
239            .view()
240            .erased_serialize(&mut <dyn erased_serde::Serializer>::erase(ser))
241            .map_err(BridgeError::SerializeView)
242    }
243}