Skip to main content

crux_core/bridge/
mod.rs

1mod formats;
2mod registry;
3mod request_serde;
4
5use facet::Facet;
6use serde::{Deserialize, Serialize};
7use std::fmt::Debug;
8use thiserror::Error;
9
10use crate::{App, Core, core::ResolveError};
11pub use formats::{BincodeFfiFormat, JsonFfiFormat};
12pub use registry::EffectId;
13pub(crate) use registry::ResolveRegistry;
14// ResolveByte is public to be accessible from crux_macros
15#[doc(hidden)]
16pub use request_serde::ResolveSerialized;
17
18/// A serialization format for the bridge FFI.
19///
20/// **Note**: While you can implement your own format for use with the [`BridgeWithSerializer`],
21/// the type generation system doesn't yet support automatically generating the shell-side support
22/// for different formats, and you'll need to bring your own solution for this.
23pub trait FfiFormat: Debug + 'static {
24    type Error: std::error::Error;
25
26    /// Serialize an instance of `T` into the provided growable byte buffer.
27    ///
28    /// # Errors
29    ///
30    /// Returns an error if serialization fails.
31    fn serialize<T: Serialize>(buffer: &mut Vec<u8>, value: &T) -> Result<(), Self::Error>;
32
33    /// Deserialize an instance of `T` from the provided byte slice.
34    ///
35    /// # Errors
36    ///
37    /// Returns an error if deserialization fails.
38    fn deserialize<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result<T, Self::Error>;
39}
40
41/// Request for a side-effect passed from the Core to the Shell. The `EffectId` links
42/// the `Request` with the corresponding call to [`Core::resolve`] to pass the data back
43/// to the [`App::update`] function (wrapped in the event provided to the capability originating the effect).
44// used in docs/internals/bridge.md
45// ANCHOR: request
46#[derive(Facet, Debug, Serialize, Deserialize)]
47pub struct Request<Eff>
48where
49    Eff: Serialize,
50{
51    pub id: EffectId,
52    pub effect: Eff,
53}
54// ANCHOR_END: request
55
56/// Bridge is a core wrapper presenting the same interface as the [`Core`] but in a
57/// serialized form, using bincode as the serialization format.
58pub struct Bridge<A, F = BincodeFfiFormat>
59where
60    A: App,
61    F: FfiFormat,
62{
63    core: Core<A>,
64    registry: ResolveRegistry<F>,
65}
66
67#[derive(Debug, Error)]
68pub enum BridgeError<F: FfiFormat = BincodeFfiFormat> {
69    #[error("could not deserialize event: {0}")]
70    DeserializeEvent(F::Error),
71    #[error("could not deserialize provided effect output: {0}")]
72    DeserializeOutput(F::Error),
73    #[error("could not process response: {0}")]
74    ProcessResponse(#[from] ResolveError),
75    #[error("could not serialize effect requests: {0}")]
76    SerializeRequests(F::Error),
77    #[error("could not serialize view model: {0}")]
78    SerializeView(F::Error),
79}
80
81impl<A, Format> Bridge<A, Format>
82where
83    A: App,
84    Format: FfiFormat,
85{
86    /// Create a new Bridge using the provided `core`.
87    pub fn new(core: Core<A>) -> Self {
88        Self {
89            core,
90            registry: ResolveRegistry::default(),
91        }
92    }
93
94    /// Receive an event from the shell.
95    ///
96    /// The `event` is serialized and will be deserialized by the core before it's passed
97    /// to your app.
98    ///
99    /// # Errors
100    ///
101    /// Returns an error if the event could not be deserialized.
102    #[deprecated(
103        since = "0.17.0",
104        note = "Bridge API returning vectors has been deprecated. Please use the 'update' method."
105    )]
106    pub fn process_event(&self, event: &[u8]) -> Result<Vec<u8>, BridgeError<Format>>
107    where
108        A::Event: for<'a> Deserialize<'a>,
109        A::Effect: crate::core::EffectFFI,
110    {
111        let mut return_buffer = vec![];
112
113        self.update(event, &mut return_buffer)?;
114
115        Ok(return_buffer)
116    }
117
118    /// Send an event from the shell.
119    ///
120    /// The `event` is serialized and will be deserialized by the core before it's passed
121    /// to your app.
122    ///
123    /// # Errors
124    ///
125    /// Returns an error if the event could not be deserialized.
126    pub fn update<'a>(
127        &self,
128        event: &'a [u8],
129        requests_out: &mut Vec<u8>,
130    ) -> Result<(), BridgeError<Format>>
131    where
132        A::Event: Deserialize<'a>,
133        A::Effect: crate::core::EffectFFI,
134    {
135        self.process(None, event, requests_out)
136    }
137
138    /// Receive a response to a capability request from the shell.
139    ///
140    /// The `output` is serialized capability output. It will be deserialized by the core.
141    ///
142    /// # Errors
143    ///
144    /// Returns an error if the response could not be deserialized.
145    ///
146    /// # Panics
147    ///
148    /// The `id` MUST match the `id` of the effect that triggered it, else the core will panic.
149    // used in docs/internals/bridge.md
150    // ANCHOR: handle_response_sig
151    #[deprecated(
152        since = "0.17.0",
153        note = "Bridge API returning vectors has been deprecated. Please use the 'resolve' method."
154    )]
155    pub fn handle_response(&self, id: u32, output: &[u8]) -> Result<Vec<u8>, BridgeError<Format>>
156    // ANCHOR_END: handle_response_sig
157    where
158        A::Event: for<'a> Deserialize<'a>,
159        A::Effect: crate::core::EffectFFI,
160    {
161        let mut return_buffer = vec![];
162
163        self.resolve(EffectId(id), output, &mut return_buffer)?;
164
165        Ok(return_buffer)
166    }
167
168    /// Provide a response to a capability request to resolve it and continue the corresponding command.
169    ///
170    /// The `output` is serialized capability output. It will be deserialized by the core.
171    ///
172    /// # Errors
173    ///
174    /// Returns an error if the response could not be deserialized.
175    ///
176    /// # Panics
177    ///
178    /// The `id` MUST match the `id` of the effect that triggered it, else the core will panic.
179    pub fn resolve<'a>(
180        &self,
181        id: EffectId,
182        response: &'a [u8],
183        requests_out: &mut Vec<u8>,
184    ) -> Result<(), BridgeError<Format>>
185    where
186        A::Event: Deserialize<'a>,
187        A::Effect: crate::core::EffectFFI,
188    {
189        self.process(Some(id), response, requests_out)
190    }
191
192    fn process<'a>(
193        &self,
194        id: Option<EffectId>,
195        data: &'a [u8],
196        requests_out: &mut Vec<u8>,
197    ) -> Result<(), BridgeError<Format>>
198    where
199        A::Event: Deserialize<'a>,
200        A::Effect: crate::core::EffectFFI,
201    {
202        let effects = match id {
203            None => {
204                let shell_event =
205                    Format::deserialize(data).map_err(BridgeError::DeserializeEvent)?;
206
207                self.core.process_event(shell_event)
208            }
209            Some(id) => {
210                self.registry.resume(id, data)?;
211
212                self.core.process()
213            }
214        };
215
216        self.process_effects(effects, requests_out)
217    }
218
219    fn process_effects(
220        &self,
221        effects: Vec<A::Effect>,
222        requests_out: &mut Vec<u8>,
223    ) -> Result<(), BridgeError<Format>>
224    where
225        A::Effect: crate::core::EffectFFI,
226    {
227        let requests: Vec<_> = effects
228            .into_iter()
229            .map(|eff| self.registry.register(eff))
230            .collect();
231
232        Format::serialize(requests_out, &requests).map_err(BridgeError::SerializeRequests)?;
233
234        Ok(())
235    }
236
237    /// Get the current state of the app's view model (serialized).
238    ///
239    /// # Errors
240    ///
241    /// Returns an error if the view model could not be serialized.
242    pub fn view(&self, view_out: &mut Vec<u8>) -> Result<(), BridgeError<Format>>
243    where
244        A::ViewModel: Serialize,
245    {
246        Format::serialize(view_out, &self.core.view()).map_err(BridgeError::SerializeView)
247    }
248}