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    ///
69    /// # Errors
70    ///
71    /// Returns an error if the event could not be deserialized.
72    pub fn process_event(&self, event: &[u8]) -> Result<Vec<u8>, BridgeError>
73    where
74        A::Event: for<'a> Deserialize<'a>,
75    {
76        let options = Self::bincode_options();
77
78        let mut deser = bincode::Deserializer::from_slice(event, options);
79
80        let mut return_buffer = vec![];
81        let mut ser = bincode::Serializer::new(&mut return_buffer, options);
82
83        self.inner.process_event(&mut deser, &mut ser)?;
84
85        Ok(return_buffer)
86    }
87
88    /// Receive a response to a capability request from the shell.
89    ///
90    /// The `output` is serialized capability output. It will be deserialized by the core.
91    ///
92    /// # Errors
93    ///
94    /// Returns an error if the response could not be deserialized.
95    ///
96    /// # Panics
97    ///
98    /// The `id` MUST match the `id` of the effect that triggered it, else the core will panic.
99    // used in docs/internals/bridge.md
100    // ANCHOR: handle_response_sig
101    pub fn handle_response(&self, id: u32, output: &[u8]) -> Result<Vec<u8>, BridgeError>
102    // ANCHOR_END: handle_response_sig
103    where
104        A::Event: for<'a> Deserialize<'a>,
105    {
106        let options = Self::bincode_options();
107
108        let mut deser = bincode::Deserializer::from_slice(output, options);
109
110        let mut return_buffer = vec![];
111        let mut ser = bincode::Serializer::new(&mut return_buffer, options);
112
113        self.inner.handle_response(id, &mut deser, &mut ser)?;
114
115        Ok(return_buffer)
116    }
117
118    /// Get the current state of the app's view model (serialized).
119    ///
120    /// # Errors
121    ///
122    /// Returns an error if the view model could not be serialized.
123    pub fn view(&self) -> Result<Vec<u8>, BridgeError> {
124        let options = Self::bincode_options();
125
126        let mut return_buffer = vec![];
127
128        self.inner
129            .view(&mut bincode::Serializer::new(&mut return_buffer, options))?;
130
131        Ok(return_buffer)
132    }
133
134    fn bincode_options() -> impl bincode::Options + Copy {
135        DefaultOptions::new()
136            .with_fixint_encoding()
137            .allow_trailing_bytes()
138    }
139}
140
141/// A bridge with a user supplied serializer
142///
143/// This is exactly the same as [`Bridge`], except instead of using the default
144/// bincode serialization, you can provide your own [`Serializer`](::serde::ser::Serializer).
145///
146/// **Warning**: the support for custom serialization is **experimental** and
147/// does not have a corresponding type generation support - you will need
148/// to write deserialization code on the shell side yourself, or generate
149/// it using separate tooling.
150// used in docs/internals/bridge.md
151// ANCHOR: bridge_with_serializer
152pub struct BridgeWithSerializer<A>
153where
154    A: App,
155{
156    core: Core<A>,
157    registry: ResolveRegistry,
158}
159// ANCHOR_END: bridge_with_serializer
160
161impl<A> BridgeWithSerializer<A>
162where
163    A: App,
164{
165    pub fn new(core: Core<A>) -> Self {
166        Self {
167            core,
168            registry: ResolveRegistry::default(),
169        }
170    }
171
172    /// Receive an event from the shell.
173    ///
174    /// The `event` is serialized and will be deserialized by the core before it's passed
175    /// to your app.
176    ///
177    /// # Errors
178    ///
179    /// Returns an error if the event could not be deserialized.
180    pub fn process_event<'de, D, S>(&self, event: D, requests_out: S) -> Result<(), BridgeError>
181    where
182        for<'a> A::Event: Deserialize<'a>,
183        D: ::serde::de::Deserializer<'de> + 'de,
184        S: ::serde::ser::Serializer,
185    {
186        let mut erased_de = <dyn erased_serde::Deserializer>::erase(event);
187        self.process(
188            None,
189            &mut erased_de,
190            &mut <dyn erased_serde::Serializer>::erase(requests_out),
191        )
192    }
193
194    /// Receive a response to a capability request from the shell.
195    ///
196    /// The `output` is serialized capability output. It will be deserialized by the core.
197    ///
198    /// # Errors
199    ///
200    /// Returns an error if the response could not be deserialized.
201    ///
202    /// # Panics
203    ///
204    /// The `id` MUST match the `id` of the effect that triggered it, else the core will panic.
205    pub fn handle_response<'de, D, S>(
206        &self,
207        id: u32,
208        response: D,
209        requests_out: S,
210    ) -> Result<(), BridgeError>
211    where
212        for<'a> A::Event: Deserialize<'a>,
213        D: ::serde::de::Deserializer<'de>,
214        S: ::serde::ser::Serializer,
215    {
216        let mut erased_response = <dyn erased_serde::Deserializer>::erase(response);
217        self.process(
218            Some(EffectId(id)),
219            &mut erased_response,
220            &mut <dyn erased_serde::Serializer>::erase(requests_out),
221        )
222    }
223
224    fn process(
225        &self,
226        id: Option<EffectId>,
227        data: &mut dyn erased_serde::Deserializer,
228        requests_out: &mut dyn erased_serde::Serializer,
229    ) -> Result<(), BridgeError>
230    where
231        A::Event: for<'a> Deserialize<'a>,
232    {
233        let effects = match id {
234            None => {
235                let shell_event =
236                    erased_serde::deserialize(data).map_err(BridgeError::DeserializeEvent)?;
237
238                self.core.process_event(shell_event)
239            }
240            Some(id) => {
241                self.registry.resume(id, data)?;
242
243                self.core.process()
244            }
245        };
246
247        let requests: Vec<_> = effects
248            .into_iter()
249            .map(|eff| self.registry.register(eff))
250            .collect();
251
252        requests
253            .erased_serialize(requests_out)
254            .map_err(BridgeError::SerializeRequests)?;
255
256        Ok(())
257    }
258
259    /// Get the current state of the app's view model (serialized).
260    ///
261    /// # Errors
262    ///
263    /// Returns an error if the view model could not be serialized.
264    pub fn view<S>(&self, ser: S) -> Result<(), BridgeError>
265    where
266        S: ::serde::ser::Serializer,
267    {
268        self.core
269            .view()
270            .erased_serialize(&mut <dyn erased_serde::Serializer>::erase(ser))
271            .map_err(BridgeError::SerializeView)
272    }
273}