use crate::adapters::WasmIdbStorage;
use crate::node::Node;
use crate::Config;
use crate::types::Value;
use std::sync::OnceLock;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::spawn_local;
static RT: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
fn runtime() -> &'static tokio::runtime::Runtime {
RT.get_or_init(|| {
tokio::runtime::Builder::new_current_thread()
.build()
.expect("failed to create tokio runtime")
})
}
#[wasm_bindgen]
pub struct Beam {
node: Node,
}
#[wasm_bindgen]
impl Beam {
#[wasm_bindgen(constructor)]
pub fn new() -> Beam {
console_error_panic_hook::set_once();
let _guard = runtime().enter();
Beam {
node: Node::new(),
}
}
pub fn new_persistent() -> Beam {
console_error_panic_hook::set_once();
let _guard = runtime().enter();
Beam {
node: Node::new_with_config(
Config::default(),
vec![Box::new(WasmIdbStorage::new())],
Vec::new(),
),
}
}
pub fn connect(&self, url: &str) {
let _guard = runtime().enter();
self.node.connect_peer_wasm(url);
}
pub fn put(&mut self, path: &str, value: &str) {
let _guard = runtime().enter();
let mut node = path.split('.').fold(self.node.clone(), |mut n, key| n.get(key));
let value = value.to_string();
spawn_local(async move {
let _ = node.put(Value::Text(value)).await;
});
}
pub fn put_num(&mut self, path: &str, value: f64) {
let _guard = runtime().enter();
let mut node = path.split('.').fold(self.node.clone(), |mut n, key| n.get(key));
spawn_local(async move {
let _ = node.put(Value::Number(value)).await;
});
}
pub fn put_bool(&mut self, path: &str, value: bool) {
let _guard = runtime().enter();
let mut node = path.split('.').fold(self.node.clone(), |mut n, key| n.get(key));
spawn_local(async move {
let _ = node.put(Value::Bit(value)).await;
});
}
pub fn put_null(&mut self, path: &str) {
let _guard = runtime().enter();
let mut node = path.split('.').fold(self.node.clone(), |mut n, key| n.get(key));
spawn_local(async move {
let _ = node.put(Value::Null).await;
});
}
#[wasm_bindgen(js_name = get)]
pub fn get(&mut self, path: &str) -> js_sys::Promise {
let _guard = runtime().enter();
let mut node = path.split('.').fold(self.node.clone(), |mut n, key| n.get(key));
wasm_bindgen_futures::future_to_promise(async move {
match node.once(None).await {
Some(Value::Text(s)) => Ok(JsValue::from(s)),
Some(Value::Number(n)) => Ok(JsValue::from(n)),
Some(Value::Bit(b)) => Ok(JsValue::from(b)),
Some(Value::Link(s)) => Ok(JsValue::from(s)),
Some(Value::Null) | None => Ok(JsValue::NULL),
}
})
}
pub fn on(&mut self, path: &str, callback: js_sys::Function) {
let _guard = runtime().enter();
let node = path.split('.').fold(self.node.clone(), |mut n, key| n.get(key));
let mut rx = node.map();
spawn_local(async move {
loop {
match rx.recv().await {
Ok((_key, value)) => {
let js_val = match value {
Value::Text(s) => JsValue::from(s),
Value::Number(n) => JsValue::from(n),
Value::Bit(b) => JsValue::from(b),
Value::Link(s) => JsValue::from(s),
Value::Null => JsValue::NULL,
};
if js_val.is_null() {
continue;
}
let _ = callback.call1(&JsValue::UNDEFINED, &js_val);
}
Err(tokio::sync::broadcast::error::RecvError::Closed) => break,
Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => continue,
}
}
});
}
pub fn stop(&mut self) {
let _guard = runtime().enter();
self.node.stop();
}
}