use std::{
collections::HashMap,
error::Error,
sync::Mutex,
time::{Duration, Instant},
};
use crate::token::BoxFuture;
pub trait ReplayGuard: Send + Sync {
fn claim<'a>(
&'a self,
binding: &'a str,
ttl: Duration,
) -> BoxFuture<'a, Result<bool, Box<dyn Error + Send + Sync>>>;
}
#[derive(Debug, Default)]
pub struct InMemoryReplayGuard {
claims: Mutex<HashMap<String, Instant>>,
}
impl ReplayGuard for InMemoryReplayGuard {
fn claim<'a>(
&'a self,
binding: &'a str,
ttl: Duration,
) -> BoxFuture<'a, Result<bool, Box<dyn Error + Send + Sync>>> {
Box::pin(async move {
let now = Instant::now();
let mut claims = self
.claims
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
claims.retain(|_, expires_at| *expires_at > now);
if claims.contains_key(binding) {
return Ok(false);
}
claims.insert(binding.to_owned(), now + ttl);
Ok(true)
})
}
}