use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Mutex;
use tokio::sync::oneshot;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RequestId(u64);
impl RequestId {
#[must_use]
pub const fn get(self) -> u64 {
self.0
}
}
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum CorrelationError {
#[error("unknown request id: {0}")]
UnknownId(u64),
#[error("request {0} already completed")]
AlreadyResolved(u64),
}
#[derive(Default)]
pub struct Correlator<R> {
next_id: AtomicU64,
pending: Mutex<HashMap<RequestId, oneshot::Sender<R>>>,
}
impl<R> Correlator<R> {
#[must_use]
pub fn new() -> Self {
Self {
next_id: AtomicU64::new(1),
pending: Mutex::new(HashMap::new()),
}
}
#[must_use]
pub fn pending_count(&self) -> usize {
self.pending.lock().map_or(0, |g| g.len())
}
}
impl<R: Send + 'static> Correlator<R> {
pub fn register(&self) -> (RequestId, oneshot::Receiver<R>) {
let id = RequestId(self.next_id.fetch_add(1, Ordering::SeqCst));
let (tx, rx) = oneshot::channel();
if let Ok(mut guard) = self.pending.lock() {
guard.insert(id, tx);
}
(id, rx)
}
pub fn resolve(&self, id: RequestId, response: R) -> Result<(), CorrelationError> {
let sender = self
.pending
.lock()
.ok()
.and_then(|mut guard| guard.remove(&id))
.ok_or(CorrelationError::UnknownId(id.get()))?;
sender
.send(response)
.map_err(|_| CorrelationError::AlreadyResolved(id.get()))
}
}
impl<R> std::fmt::Debug for Correlator<R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Correlator")
.field("pending", &self.pending_count())
.finish()
}
}