use crossbeam::channel::{self, Receiver, Sender};
use twinleaf::device::{util, DeviceRoute, RpcClient, RpcList, RpcValue};
use crate::tui::rpc_palette::{RpcReq, RpcResp};
pub enum RpcWorkerReq {
FetchList(DeviceRoute),
Execute(RpcReq),
}
pub enum RpcWorkerResp {
List(RpcList),
ListErr { route: DeviceRoute, error: String },
RpcResult(RpcResp),
}
pub fn exec_rpc(client: &RpcClient, req: &RpcReq) -> Result<String, String> {
let kind = req.req_type.unwrap_or_else(|| {
let meta = req.meta.or_else(|| {
client
.rpc::<&String, u16>(&req.route, "rpc.info", &req.method)
.ok()
});
util::resolve_arg_type(meta, &req.method)
});
let payload = if let Some(ref s) = req.arg {
util::rpc_encode_arg(s, &kind).map_err(|e| e.to_string())?
} else {
Vec::new()
};
let reply_bytes = client
.raw_rpc(&req.route, &req.method, &payload)
.map_err(|e| e.to_string())?;
if reply_bytes.is_empty() {
return Ok("OK".to_string());
}
let reply_kind = req.rep_type.unwrap_or(kind);
let value = util::rpc_decode_reply(&reply_bytes, &reply_kind).map_err(|e| e.to_string())?;
Ok(match &value {
RpcValue::Str(s) => format!("\"{}\" {:?}", s, s.as_bytes()),
RpcValue::Bytes(b) => format!("{:?}", b),
other => format!("{}", other),
})
}
pub fn spawn_rpc_worker(client: RpcClient) -> (Sender<RpcWorkerReq>, Receiver<RpcWorkerResp>) {
let (req_tx, req_rx) = channel::unbounded::<RpcWorkerReq>();
let (resp_tx, resp_rx) = channel::unbounded::<RpcWorkerResp>();
std::thread::spawn(move || {
while let Ok(req) = req_rx.recv() {
let resp = match req {
RpcWorkerReq::FetchList(route) => match client.rpc_list(&route) {
Ok(list) => Some(RpcWorkerResp::List(list)),
Err(err) => Some(RpcWorkerResp::ListErr {
route,
error: err.to_string(),
}),
},
RpcWorkerReq::Execute(rpc_req) => {
let result = exec_rpc(&client, &rpc_req);
Some(RpcWorkerResp::RpcResult(RpcResp { result }))
}
};
if let Some(resp) = resp {
if resp_tx.send(resp).is_err() {
return;
}
}
}
});
(req_tx, resp_rx)
}