use crate::types::Message;
use std::sync::LazyLock;
use tokio::sync::mpsc::{OwnedPermit, Sender};
pub(crate) struct RequestSink {
tx: Sender<Message>,
#[cfg_attr(not(feature = "http-server"), allow(dead_code))]
ack: Option<OwnedPermit<Message>>,
}
pub(crate) static REQUEST_NOTIFICATIONS: LazyLock<dashmap::DashMap<uuid::Uuid, RequestSink>> =
LazyLock::new(dashmap::DashMap::new);
impl RequestSink {
#[cfg_attr(not(feature = "tracing"), allow(dead_code))]
#[inline]
pub(crate) fn try_send(&self, msg: Message) -> Result<(), ()> {
self.tx.try_send(msg).map_err(|_| ())
}
}
#[cfg(any(feature = "http-server", all(test, feature = "tracing")))]
pub(crate) async fn register(
id: uuid::Uuid,
capacity: usize,
reserve_ack: bool,
) -> tokio::sync::mpsc::Receiver<Message> {
let (tx, rx) = tokio::sync::mpsc::channel::<Message>(capacity + usize::from(reserve_ack));
let ack = match reserve_ack {
true => tx.clone().reserve_owned().await.ok(),
false => None,
};
REQUEST_NOTIFICATIONS.insert(id, RequestSink { tx, ack });
rx
}
#[cfg(feature = "http-server")]
pub(crate) fn take_ack_permit(id: &uuid::Uuid) -> Option<OwnedPermit<Message>> {
REQUEST_NOTIFICATIONS
.get_mut(id)
.and_then(|mut s| s.ack.take())
}
#[cfg(any(feature = "http-server", all(test, feature = "tracing")))]
pub(crate) fn unregister(id: &uuid::Uuid) {
REQUEST_NOTIFICATIONS.remove(id);
}
#[cfg(feature = "http-server")]
pub(crate) fn get(id: &uuid::Uuid) -> Option<Sender<Message>> {
REQUEST_NOTIFICATIONS.get(id).map(|s| s.tx.clone())
}
#[cfg(all(test, feature = "http-server"))]
mod tests {
use super::*;
use crate::types::notification::Notification;
#[tokio::test]
async fn it_routes_to_a_registered_sink() {
let id = uuid::Uuid::new_v4();
let mut rx = register(id, 4, false).await;
let sink = get(&id).expect("sink should be registered");
sink.send(Message::Notification(Notification::new("test", None)))
.await
.unwrap();
assert!(rx.recv().await.is_some());
unregister(&id);
assert!(get(&id).is_none());
}
#[tokio::test]
async fn it_reports_a_dropped_receiver_as_closed() {
let id = uuid::Uuid::new_v4();
let rx = register(id, 4, false).await;
let sink = get(&id).expect("sink should be registered");
drop(rx);
sink.closed().await;
unregister(&id);
}
}