1//! The FFI surface the Swift shell calls, via BoltFFI. Mirrors the Crux
2//! counter example's `ffi.rs`: a `Bridge` that takes/returns bincode bytes.
4//! Nothing here may unwind. A panic crossing the Rust/Swift boundary is
5//! undefined behavior, not a crash report — so every exported method runs its
6//! body inside `catch_unwind` and turns a panic into an empty response. The
7//! shell already tolerates an empty effect list (it just does nothing) and an
8//! empty view (it renders the previous one), so a bug degrades into a stalled
9//! UI rather than a corrupted process.
11//! Note the release profile deliberately does *not* set `panic = "abort"`:
12//! aborting would fire before `catch_unwind` ever ran, which is strictly worse
15#![allow(clippy::used_underscore_items)]
17use std::panic::{catch_unwind, AssertUnwindSafe};
20 bridge::{Bridge, EffectId},
26/// The main interface used by the shell.
28 core: Bridge<Skrepka>,
31impl Default for CoreFFI {
32 fn default() -> Self {
37/// Run `f`, converting a panic (or a bridge error) into an empty byte vec.
39/// `AssertUnwindSafe` is sound here because a poisoned `Bridge` is never read
40/// again for correctness: the worst case is a dropped effect batch, and the next
41/// event starts a fresh `update`.
43/// NOTE: A panic mid-`update` leaves the `Model` in a partially-mutated state.
44/// The next `update` call operates on this potentially-corrupted state. A
45/// proper fix requires snapshotting the Model before `update` and restoring on
46/// panic, or marking the Bridge as poisoned. This is an accepted limitation.
47fn guard(f: impl FnOnce() -> Vec<u8>) -> Vec<u8> {
48 catch_unwind(AssertUnwindSafe(f)).unwrap_or_default()
54 pub fn new() -> Self {
56 core: Bridge::new(Core::new()),
60 /// Send an event to the app and return the resulting effects (bincode).
61 /// Empty on a serialization failure — see the module docs.
63 pub fn update(&self, data: &[u8]) -> Vec<u8> {
65 let mut effects = Vec::new();
66 match self.core.update(data, &mut effects) {
73 /// Resolve an effect request and return the next batch of effects.
75 pub fn resolve(&self, id: u32, data: &[u8]) -> Vec<u8> {
77 let mut effects = Vec::new();
78 match self.core.resolve(EffectId(id), data, &mut effects) {
85 /// Get the current serialized `ViewModel`.
87 pub fn view(&self) -> Vec<u8> {
89 let mut view_model = Vec::new();
90 match self.core.view(&mut view_model) {
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list