use std::cell::RefCell;
use std::rc::Rc;
use serde::{Deserialize, Serialize};
use wasm_bindgen::JsValue;
use web_sys::RtcDataChannel;
use super::shared_fs;
use super::webrtc::Peer;
type Tx = Rc<RefCell<Option<RtcDataChannel>>>;
#[derive(Serialize, Deserialize)]
enum SyncMsg {
Manifest(Vec<String>),
Want(String),
File { name: String, data: Vec<u8> },
}
fn send_msg(tx: &Tx, msg: &SyncMsg) {
let Ok(bytes) = serde_json::to_vec(msg) else {
return;
};
if let Some(ch) = tx.borrow().as_ref() {
let _ = ch.send_with_u8_array(&bytes);
}
}
fn handle_message(bytes: Vec<u8>, tx: Tx) {
let Ok(msg) = serde_json::from_slice::<SyncMsg>(&bytes) else {
return;
};
wasm_bindgen_futures::spawn_local(async move {
match msg {
SyncMsg::Manifest(remote) => {
let local: Vec<String> = shared_fs::apex_list()
.await
.into_iter()
.map(|e| e.name)
.collect();
for name in remote {
if !local.iter().any(|n| n == &name) {
send_msg(&tx, &SyncMsg::Want(name));
}
}
}
SyncMsg::Want(name) => {
if let Ok(Some(data)) = shared_fs::apex_read(&name).await {
send_msg(&tx, &SyncMsg::File { name, data });
}
}
SyncMsg::File { name, data } => {
let _ = shared_fs::apex_write(&name, &data).await;
}
}
});
}
pub(crate) struct SharedFsSync {
peer: Peer,
tx: Tx,
}
impl SharedFsSync {
pub(crate) async fn offer() -> Result<(Self, String), JsValue> {
let tx: Tx = Rc::new(RefCell::new(None));
let tx_cb = tx.clone();
let (peer, sdp) = Peer::offer(move |bytes| handle_message(bytes, tx_cb.clone())).await?;
*tx.borrow_mut() = Some(peer.sender());
Ok((Self { peer, tx }, sdp))
}
pub(crate) async fn answer(offer_sdp: &str) -> Result<(Self, String), JsValue> {
let tx: Tx = Rc::new(RefCell::new(None));
let tx_cb = tx.clone();
let (peer, sdp) =
Peer::answer(offer_sdp, move |bytes| handle_message(bytes, tx_cb.clone())).await?;
*tx.borrow_mut() = Some(peer.sender());
Ok((Self { peer, tx }, sdp))
}
pub(crate) async fn accept_answer(&self, answer_sdp: &str) -> Result<(), JsValue> {
self.peer.accept_answer(answer_sdp).await
}
pub(crate) async fn start(&self) {
let names: Vec<String> = shared_fs::apex_list()
.await
.into_iter()
.map(|e| e.name)
.collect();
send_msg(&self.tx, &SyncMsg::Manifest(names));
}
pub(crate) fn is_open(&self) -> bool {
self.peer.is_open()
}
}