use crate::error::ClientError;
use crate::transport::HolochainTransport;
use crate::types::{ConnectConfig, ConnectionStatus};
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"], js_name = invoke, catch)]
async fn tauri_invoke(cmd: &str, args: JsValue) -> Result<JsValue, JsValue>;
}
#[derive(Serialize)]
struct CallZomeArgs {
role: String,
zome: String,
fn_name: String,
payload: Vec<u8>,
}
#[derive(Deserialize)]
struct CallZomeResponse {
data: Vec<u8>,
}
pub struct TauriIpcTransport {
status: Rc<RefCell<ConnectionStatus>>,
}
impl TauriIpcTransport {
pub fn new() -> Self {
Self {
status: Rc::new(RefCell::new(ConnectionStatus::Disconnected)),
}
}
}
impl Default for TauriIpcTransport {
fn default() -> Self {
Self::new()
}
}
impl HolochainTransport for TauriIpcTransport {
fn connect(
&self,
config: ConnectConfig,
) -> Pin<Box<dyn Future<Output = Result<(), ClientError>>>> {
let status = Rc::clone(&self.status);
Box::pin(async move {
*status.borrow_mut() = ConnectionStatus::Connecting;
let connect_args = serde_wasm_bindgen::to_value(&serde_json::json!({
"url": config.url,
"app_id": config.app_id,
"auth_token": config.auth_token,
}))
.map_err(|e| ClientError::SerializationError(e.to_string()))?;
match tauri_invoke("holochain_connect", connect_args).await {
Ok(_) => {
*status.borrow_mut() = ConnectionStatus::Connected;
Ok(())
}
Err(e) => {
let msg = format!("{:?}", e);
*status.borrow_mut() = ConnectionStatus::Error(msg.clone());
Err(ClientError::ConnectionFailed(msg))
}
}
})
}
fn call_zome(
&self,
role_name: &str,
zome_name: &str,
fn_name: &str,
payload: Vec<u8>,
) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, ClientError>>>> {
let status = Rc::clone(&self.status);
let role = role_name.to_string();
let zome = zome_name.to_string();
let fn_name = fn_name.to_string();
Box::pin(async move {
if *status.borrow() != ConnectionStatus::Connected {
return Err(ClientError::NotConnected);
}
let args = CallZomeArgs {
role,
zome,
fn_name,
payload,
};
let args_js = serde_wasm_bindgen::to_value(&args)
.map_err(|e| ClientError::SerializationError(e.to_string()))?;
let result = tauri_invoke("call_zome", args_js).await.map_err(|e| {
let msg = js_value_to_string(&e);
*status.borrow_mut() = ConnectionStatus::Error(msg.clone());
ClientError::ZomeCallFailed(msg)
})?;
let response: CallZomeResponse = serde_wasm_bindgen::from_value(result)
.map_err(|e| ClientError::InvalidResponse(e.to_string()))?;
Ok(response.data)
})
}
fn status(&self) -> ConnectionStatus {
self.status.borrow().clone()
}
fn disconnect(&self) {
*self.status.borrow_mut() = ConnectionStatus::Disconnected;
}
}
fn js_value_to_string(val: &JsValue) -> String {
if let Some(s) = val.as_string() {
s
} else {
format!("{:?}", val)
}
}