bitwarden_ipc/wasm/
communication_backend.rs1use std::sync::Arc;
2
3use bitwarden_error::bitwarden_error;
4use bitwarden_threading::ThreadBoundRunner;
5use thiserror::Error;
6use tokio::sync::RwLock;
7use wasm_bindgen::prelude::*;
8
9use crate::{
10 message::{IncomingMessage, OutgoingMessage},
11 traits::{CommunicationBackend, CommunicationBackendReceiver},
12};
13
14#[allow(missing_docs)]
15#[derive(Debug, Error)]
16#[bitwarden_error(basic)]
17#[error("Failed to deserialize incoming message: {0}")]
18pub struct DeserializeError(String);
19
20#[allow(missing_docs)]
21#[derive(Debug, Error)]
22#[bitwarden_error(basic)]
23#[error("Incoming message channel failed: {0}")]
24pub struct ChannelError(String);
25
26#[wasm_bindgen(typescript_custom_section)]
27const TS_CUSTOM_TYPES: &'static str = r#"
28export interface IpcCommunicationBackendSender {
29 send(message: OutgoingMessage): Promise<void>;
30}
31"#;
32
33#[wasm_bindgen]
34extern "C" {
35 #[wasm_bindgen(js_name = IpcCommunicationBackendSender, typescript_type = "IpcCommunicationBackendSender")]
37 pub type JsCommunicationBackendSender;
38
39 #[wasm_bindgen(catch, method, structural)]
41 pub async fn send(
42 this: &JsCommunicationBackendSender,
43 message: OutgoingMessage,
44 ) -> Result<(), JsValue>;
45
46 #[wasm_bindgen(catch, method, structural)]
48 pub async fn receive(this: &JsCommunicationBackendSender) -> Result<JsValue, JsValue>;
49}
50
51#[wasm_bindgen(js_name = IpcCommunicationBackend)]
53pub struct JsCommunicationBackend {
54 sender: Arc<ThreadBoundRunner<JsCommunicationBackendSender>>,
55 receive_rx: tokio::sync::broadcast::Receiver<IncomingMessage>,
56 receive_tx: tokio::sync::broadcast::Sender<IncomingMessage>,
57}
58
59impl Clone for JsCommunicationBackend {
60 fn clone(&self) -> Self {
61 Self {
62 sender: self.sender.clone(),
63 receive_rx: self.receive_rx.resubscribe(),
64 receive_tx: self.receive_tx.clone(),
65 }
66 }
67}
68
69#[wasm_bindgen(js_class = IpcCommunicationBackend)]
70impl JsCommunicationBackend {
71 #[wasm_bindgen(constructor)]
73 pub fn new(sender: JsCommunicationBackendSender) -> Self {
74 let (receive_tx, receive_rx) = tokio::sync::broadcast::channel(20);
75 Self {
76 sender: Arc::new(ThreadBoundRunner::new(sender)),
77 receive_rx,
78 receive_tx,
79 }
80 }
81
82 pub fn receive(&self, message: IncomingMessage) -> Result<(), JsValue> {
84 self.receive_tx
85 .send(message)
86 .map_err(|e| ChannelError(e.to_string()))?;
87 Ok(())
88 }
89}
90
91impl CommunicationBackend for JsCommunicationBackend {
92 type SendError = String;
93 type Receiver = RwLock<tokio::sync::broadcast::Receiver<IncomingMessage>>;
94
95 async fn send(&self, message: OutgoingMessage) -> Result<(), Self::SendError> {
96 let result = self
97 .sender
98 .run_in_thread(|sender| async move {
99 sender.send(message).await.map_err(|e| format!("{:?}", e))
100 })
101 .await;
102
103 result.map_err(|e| e.to_string())?
104 }
105
106 async fn subscribe(&self) -> Self::Receiver {
107 RwLock::new(self.receive_rx.resubscribe())
108 }
109}
110
111impl CommunicationBackendReceiver for RwLock<tokio::sync::broadcast::Receiver<IncomingMessage>> {
112 type ReceiveError = String;
113
114 async fn receive(&self) -> Result<IncomingMessage, Self::ReceiveError> {
115 self.write().await.recv().await.map_err(|e| e.to_string())
116 }
117}