use std::{fmt, sync::Arc, time::Duration};
use dashmap::DashMap;
use thiserror::Error;
use tokio::time::sleep;
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("received unexpected outputs")]
pub struct UnexpectedOutputs;
#[derive(Clone)]
pub struct Wallet<K, O> {
timeout: Duration,
pending: Arc<DashMap<K, Vec<O>>>, }
impl<K: fmt::Debug + std::cmp::Eq, O: fmt::Debug> fmt::Debug for Wallet<K, O>
where
K: fmt::Debug + std::cmp::Eq + std::hash::Hash,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Wallet {{\n\ttimeout: {:?},\n\tpending: {:?}\n}}",
self.timeout, self.pending
)
}
}
impl<K, O> Wallet<K, O>
where
K: std::hash::Hash + std::cmp::Eq,
K: Clone + Send + Sync + 'static,
O: std::cmp::PartialEq + Sync + Send + 'static,
{
pub fn new(timeout: Duration) -> Self {
Wallet {
timeout,
pending: Default::default(),
}
}
pub fn add_outputs(
&self,
key: K,
outputs: Vec<O>,
) -> impl std::future::Future<Output = ()> + Send + 'static {
let key_inner = key.clone();
self.pending.insert(key, outputs);
let pending_inner = self.pending.clone();
let timeout_inner = self.timeout;
async move {
sleep(timeout_inner).await;
pending_inner.remove(&key_inner);
}
}
pub fn recv_outputs(&self, key: &K, outputs: &[O]) -> Result<(), UnexpectedOutputs> {
let check_subset = |_: &K, expected_outputs: &Vec<O>| {
expected_outputs
.iter()
.all(|output| outputs.contains(output))
};
if self.pending.remove_if(key, check_subset).is_some() {
Ok(())
} else {
Err(UnexpectedOutputs)
}
}
}