use std::collections::HashMap;
use std::sync::{
Mutex,
atomic::{AtomicU64, Ordering},
};
use bytes::Bytes;
use ruststream::{Headers, RawMessage, testing::Coordinator};
use tokio::sync::mpsc;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct SubscriptionId(u64);
#[derive(Debug, Clone)]
pub(crate) struct Delivery {
pub(crate) payload: Bytes,
pub(crate) headers: Headers,
}
pub(crate) type DeliverySender = mpsc::UnboundedSender<Delivery>;
pub(crate) type DeliveryReceiver = mpsc::UnboundedReceiver<Delivery>;
struct Subscription {
address: String,
sender: DeliverySender,
}
#[derive(Default)]
struct RouterState {
subscriptions: HashMap<SubscriptionId, Subscription>,
log: HashMap<String, Vec<RawMessage>>,
}
#[derive(Default)]
pub(crate) struct AddressRouter {
state: Mutex<RouterState>,
next_id: AtomicU64,
}
impl AddressRouter {
pub(crate) fn subscribe(
&self,
address: String,
) -> (SubscriptionId, DeliverySender, DeliveryReceiver) {
let (tx, rx) = mpsc::unbounded_channel();
let id = SubscriptionId(self.next_id.fetch_add(1, Ordering::Relaxed));
self.state
.lock()
.expect("pulsar test router mutex poisoned")
.subscriptions
.insert(
id,
Subscription {
address,
sender: tx.clone(),
},
);
(id, tx, rx)
}
pub(crate) fn unsubscribe(&self, id: SubscriptionId) {
self.state
.lock()
.expect("pulsar test router mutex poisoned")
.subscriptions
.remove(&id);
}
pub(crate) fn publish(
&self,
address: &str,
payload: Bytes,
headers: Headers,
coordinator: Option<&Coordinator>,
) {
let snapshot = RawMessage::new(address, payload.clone()).with_headers(headers.clone());
let mut to_notify: Vec<DeliverySender> = Vec::new();
{
let mut state = self
.state
.lock()
.expect("pulsar test router mutex poisoned");
state
.log
.entry(address.to_owned())
.or_default()
.push(snapshot);
for sub in state.subscriptions.values() {
if sub.address == address {
to_notify.push(sub.sender.clone());
}
}
}
let delivery = Delivery { payload, headers };
for tx in to_notify {
if tx.send(delivery.clone()).is_ok()
&& let Some(coordinator) = coordinator
{
coordinator.enqueued();
}
}
}
pub(crate) fn published(&self, address: &str) -> Vec<RawMessage> {
self.state
.lock()
.expect("pulsar test router mutex poisoned")
.log
.get(address)
.cloned()
.unwrap_or_default()
}
pub(crate) fn clear(&self) {
let mut state = self
.state
.lock()
.expect("pulsar test router mutex poisoned");
state.subscriptions.clear();
state.log.clear();
}
}
impl std::fmt::Debug for AddressRouter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let state = self
.state
.lock()
.expect("pulsar test router mutex poisoned");
f.debug_struct("AddressRouter")
.field("subscriptions", &state.subscriptions.len())
.field("logged_addresses", &state.log.len())
.finish_non_exhaustive()
}
}