Skip to main content

infinity_bridge_wasm/
bridge.rs

1use std::cell::RefCell;
2use std::marker::PhantomData;
3use std::rc::Rc;
4
5use infinity_bridge_wire::{BridgeError, EventPayload, WireMsg};
6use serde_json::Value;
7
8use crate::backend::CommBusBackend;
9
10pub struct BridgeConfig {
11    pub call_event: String,
12    pub response_event: String,
13}
14
15impl BridgeConfig {
16    pub fn new(call_event: impl Into<String>, response_event: impl Into<String>) -> Self {
17        Self {
18            call_event: call_event.into(),
19            response_event: response_event.into(),
20        }
21    }
22}
23
24pub trait BridgeHandler: 'static {
25    /// Handle a command (request/response). Must return a result.
26    ///
27    /// `name` is the optional command name from the wire message.
28    /// `payload` is the JSON payload.
29    fn on_command(&self, name: Option<&str>, payload: &Value) -> Result<Value, String>;
30
31    /// Handle a fire-and-forget event from the host.
32    ///
33    /// Default implementation does nothing. Override to process events.
34    fn on_event(&self, _name: &str, _data: &Value) {}
35}
36
37impl<F> BridgeHandler for F
38where
39    F: Fn(Option<&str>, &Value) -> Result<Value, String> + 'static,
40{
41    fn on_command(&self, name: Option<&str>, payload: &Value) -> Result<Value, String> {
42        (self)(name, payload)
43    }
44}
45
46type CommandFn = Box<dyn Fn(&Value) -> Result<Value, String>>;
47type EventFn = Box<dyn Fn(&Value)>;
48
49/// Declarative command router for the WASM bridge.
50///
51/// Maps command names to handlers, with an optional fallback for unnamed
52/// or unrecognized commands.
53///
54/// ```rust,ignore
55/// use infinity_bridge_wasm::Router;
56/// use serde_json::{json, Value};
57/// use std::cell::RefCell;
58///
59/// let state = RefCell::new(MyState::new());
60///
61/// let router = Router::new()
62///     .command("get_state", {
63///         let state = state.clone();
64///         move |_payload: &Value| {
65///             let s = state.borrow();
66///             Ok(json!({ "temp": s.temperature }))
67///         }
68///     })
69///     .command("set_config", {
70///         let state = state.clone();
71///         move |payload: &Value| {
72///             let mut s = state.borrow_mut();
73///             s.apply_config(payload);
74///             Ok(json!({"ok": true}))
75///         }
76///     })
77///     .event("config_updated", {
78///         let state = state.clone();
79///         move |data: &Value| {
80///             let mut s = state.borrow_mut();
81///             s.apply_config(data);
82///         }
83///     })
84///     .fallback(|name, payload| {
85///         Err(format!("unknown command: {}", name.unwrap_or("<unnamed>")))
86///     });
87/// ```
88pub struct Router {
89    commands: Vec<(&'static str, CommandFn)>,
90    events: Vec<(&'static str, EventFn)>,
91    fallback: Option<Box<dyn Fn(Option<&str>, &Value) -> Result<Value, String>>>,
92}
93
94impl Router {
95    pub fn new() -> Self {
96        Self {
97            commands: Vec::new(),
98            events: Vec::new(),
99            fallback: None,
100        }
101    }
102
103    pub fn command(
104        mut self,
105        name: &'static str,
106        handler: impl Fn(&Value) -> Result<Value, String> + 'static,
107    ) -> Self {
108        self.commands.push((name, Box::new(handler)));
109        self
110    }
111
112    pub fn event(mut self, name: &'static str, handler: impl Fn(&Value) + 'static) -> Self {
113        self.events.push((name, Box::new(handler)));
114        self
115    }
116
117    pub fn fallback(
118        mut self,
119        handler: impl Fn(Option<&str>, &Value) -> Result<Value, String> + 'static,
120    ) -> Self {
121        self.fallback = Some(Box::new(handler));
122        self
123    }
124}
125
126impl BridgeHandler for Router {
127    fn on_command(&self, name: Option<&str>, payload: &Value) -> Result<Value, String> {
128        if let Some(name) = name {
129            for (cmd_name, handler) in &self.commands {
130                if *cmd_name == name {
131                    return handler(payload);
132                }
133            }
134        }
135        if let Some(ref fallback) = self.fallback {
136            return fallback(name, payload);
137        }
138
139        Err(format!("UNKNOWN_COMMAND: {}", name.unwrap_or("<unnamed>")))
140    }
141
142    fn on_event(&self, name: &str, data: &Value) {
143        for (evt_name, handler) in &self.events {
144            if *evt_name == name {
145                handler(data);
146                return;
147            }
148        }
149    }
150}
151
152#[derive(serde::Deserialize)]
153struct CommBusEnvelope {
154    #[serde(rename = "requestId")]
155    request_id: String,
156    payload: Value,
157}
158
159#[derive(serde::Serialize)]
160struct CommBusResponse {
161    #[serde(rename = "requestId")]
162    request_id: String,
163    ok: bool,
164    #[serde(skip_serializing_if = "Option::is_none")]
165    response: Option<Value>,
166    #[serde(skip_serializing_if = "Option::is_none")]
167    error: Option<String>,
168}
169
170struct BridgeShared {
171    response_event: String,
172}
173
174pub struct Bridge<B: CommBusBackend> {
175    _subscription: B::Subscription,
176    shared: Rc<RefCell<BridgeShared>>,
177    _marker: PhantomData<B>,
178}
179
180impl<B: CommBusBackend> Bridge<B> {
181    pub fn new(config: BridgeConfig, handler: impl BridgeHandler) -> Result<Self, BridgeError> {
182        let shared = Rc::new(RefCell::new(BridgeShared {
183            response_event: config.response_event.clone(),
184        }));
185
186        let shared_for_cb = Rc::clone(&shared);
187
188        let subscription = B::subscribe(&config.call_event, move |raw| {
189            Self::dispatch(&shared_for_cb, &handler, raw);
190        })
191        .map_err(|e| BridgeError::transport(format!("CommBus subscribe failed: {e}")))?;
192
193        Ok(Self {
194            _subscription: subscription,
195            shared,
196            _marker: PhantomData,
197        })
198    }
199
200    fn dispatch(shared: &Rc<RefCell<BridgeShared>>, handler: &impl BridgeHandler, raw: &str) {
201        let envelope: CommBusEnvelope = match serde_json::from_str(raw) {
202            Ok(e) => e,
203            Err(e) => {
204                eprintln!("[msfs-bridge] Failed to parse CommBus envelope: {e}");
205                return;
206            }
207        };
208
209        let payload_str = match serde_json::to_string(&envelope.payload) {
210            Ok(s) => s,
211            Err(_) => {
212                Self::send_response(
213                    shared,
214                    &envelope.request_id,
215                    Err("internal: re-serialize failed".into()),
216                );
217                return;
218            }
219        };
220
221        match WireMsg::from_json(&payload_str) {
222            Ok(WireMsg::Cmd(cmd)) => {
223                let result = handler.on_command(cmd.name.as_deref(), &cmd.payload);
224                Self::send_response(shared, &envelope.request_id, result);
225            }
226            Ok(WireMsg::Event(evt)) => {
227                handler.on_event(&evt.name, &evt.data);
228            }
229            _ => {
230                let result = handler.on_command(None, &envelope.payload);
231                Self::send_response(shared, &envelope.request_id, result);
232            }
233        }
234    }
235
236    fn send_response(
237        shared: &Rc<RefCell<BridgeShared>>,
238        request_id: &str,
239        result: Result<Value, String>,
240    ) {
241        let response = CommBusResponse {
242            request_id: request_id.to_string(),
243            ok: result.is_ok(),
244            response: result.as_ref().ok().cloned(),
245            error: result.err(),
246        };
247
248        let resp_json = match serde_json::to_string(&response) {
249            Ok(j) => j,
250            Err(e) => {
251                eprintln!("[msfs-bridge] Failed to serialize response: {e}");
252                return;
253            }
254        };
255
256        let event_name = shared.borrow().response_event.clone();
257
258        if let Err(e) = B::call(&event_name, &resp_json) {
259            eprintln!("[msfs-bridge] CommBus send failed: {e}");
260        }
261    }
262
263    pub fn emit(&self, name: impl Into<String>, data: Value) -> Result<(), BridgeError> {
264        let msg = WireMsg::Event(EventPayload::new(name, data));
265        let json = msg.to_json()?;
266
267        let event_name = self.shared.borrow().response_event.clone();
268
269        B::call(&event_name, &json)
270            .map_err(|e| BridgeError::transport(format!("CommBus send failed: {e}")))
271    }
272}