use std::time::Duration;
use tokio::sync::{
mpsc::{unbounded_channel, UnboundedSender},
OnceCell,
};
mod context;
use context::ProviderContext;
use crate::{error::Error, info::LedgerInfo, transport::Transport, Exchange, Filters};
pub struct LedgerProvider {
req_tx: ReqChannel,
}
#[derive(Debug)]
pub struct LedgerHandle {
pub info: LedgerInfo,
index: usize,
req_tx: ReqChannel,
}
#[derive(Clone, Debug, PartialEq)]
pub enum LedgerReq {
List(Filters),
Connect(LedgerInfo),
Req(usize, Vec<u8>, Duration),
Close(usize),
}
#[derive(Debug)]
pub enum LedgerResp {
Devices(Vec<LedgerInfo>),
Handle(usize),
Resp(Vec<u8>),
Error(Error),
}
pub type ReqChannel = UnboundedSender<(LedgerReq, UnboundedSender<LedgerResp>)>;
static PROVIDER_CTX: OnceCell<ProviderContext> = OnceCell::const_new();
impl LedgerProvider {
pub async fn init() -> Self {
let ctx = PROVIDER_CTX
.get_or_init(|| async { ProviderContext::new().await })
.await;
Self {
req_tx: ctx.req_tx(),
}
}
}
#[cfg_attr(not(feature = "unstable_async_trait"), async_trait::async_trait)]
impl Transport for LedgerProvider {
type Device = LedgerHandle;
type Info = LedgerInfo;
type Filters = Filters;
async fn list(&mut self, filters: Filters) -> Result<Vec<LedgerInfo>, Error> {
let (tx, mut rx) = unbounded_channel::<LedgerResp>();
self.req_tx
.send((LedgerReq::List(filters), tx))
.map_err(|_| Error::Unknown)?;
match rx.recv().await {
Some(LedgerResp::Devices(i)) => Ok(i),
Some(LedgerResp::Error(e)) => Err(e),
_ => Err(Error::Unknown),
}
}
async fn connect(&mut self, info: LedgerInfo) -> Result<LedgerHandle, Error> {
let (tx, mut rx) = unbounded_channel::<LedgerResp>();
self.req_tx
.send((LedgerReq::Connect(info.clone()), tx))
.map_err(|_| Error::Unknown)?;
match rx.recv().await {
Some(LedgerResp::Handle(index)) => Ok(LedgerHandle {
info,
index,
req_tx: self.req_tx.clone(),
}),
Some(LedgerResp::Error(e)) => Err(e),
_ => Err(Error::Unknown),
}
}
}
#[cfg_attr(not(feature = "unstable_async_trait"), async_trait::async_trait)]
impl Exchange for LedgerHandle {
async fn exchange(&mut self, command: &[u8], timeout: Duration) -> Result<Vec<u8>, Error> {
let (tx, mut rx) = unbounded_channel::<LedgerResp>();
self.req_tx
.send((LedgerReq::Req(self.index, command.to_vec(), timeout), tx))
.map_err(|_| Error::Unknown)?;
match rx.recv().await {
Some(LedgerResp::Resp(data)) => Ok(data),
Some(LedgerResp::Error(e)) => Err(e),
_ => Err(Error::Unknown),
}
}
}
impl Drop for LedgerHandle {
fn drop(&mut self) {
let (tx, _rx) = unbounded_channel::<LedgerResp>();
let _ = self.req_tx.send((LedgerReq::Close(self.index), tx));
}
}