use tokio::sync::{mpsc, oneshot};
use crate::{APDUAnswer, APDUCommand, LedgerError};
mod error;
pub use error::NativeTransportError;
pub mod hid;
use hid::TransportNativeHID;
struct APDUExchange {
command: APDUCommand,
answer: oneshot::Sender<Result<APDUAnswer, LedgerError>>,
}
impl APDUExchange {
fn new(command: APDUCommand) -> (Self, oneshot::Receiver<Result<APDUAnswer, LedgerError>>) {
let (tx, rx) = oneshot::channel();
(
Self {
command,
answer: tx,
},
rx,
)
}
}
struct LedgerTask {
ledger: TransportNativeHID,
rx: tokio::sync::mpsc::Receiver<APDUExchange>,
}
impl LedgerTask {
fn new(ledger: TransportNativeHID) -> (Self, tokio::sync::mpsc::Sender<APDUExchange>) {
let (tx, rx) = tokio::sync::mpsc::channel(1);
(Self { ledger, rx }, tx)
}
fn spawn(mut self) {
let fut = async move {
while let Some(exchange) = self.rx.recv().await {
let answer = self.ledger.exchange(&exchange.command);
let answer = exchange.answer.send(answer);
if let Err(Err(err)) = answer {
tracing::error!(err = %err, "could not send answer to exchange");
}
}
};
std::thread::spawn(|| {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(fut);
});
}
}
#[derive(Debug)]
pub struct LedgerHandle {
tx: mpsc::Sender<APDUExchange>,
}
impl LedgerHandle {
pub fn init() -> Result<Self, LedgerError> {
let ledger = TransportNativeHID::new()?;
let (task, tx) = LedgerTask::new(ledger);
task.spawn();
Ok(Self { tx })
}
pub async fn exchange(&self, apdu: APDUCommand) -> Result<APDUAnswer, LedgerError> {
let (exchange, rx) = APDUExchange::new(apdu);
self.tx
.send(exchange)
.await
.map_err(|_| LedgerError::BackendGone)?;
rx.await.map_err(|_| LedgerError::BackendGone)?
}
}