use monoloop_contracts::InterpreterOutputEvent;
use tokio::sync::mpsc;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct SubscriberId(String);
impl SubscriberId {
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SubscriptionStatus {
Opened,
Closing,
Gap(SubscriptionGap),
Lost,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SubscriptionGap {
pub expected: u64,
pub observed: Option<u64>,
}
#[derive(Clone, Debug)]
pub struct DeliveredEvent {
pub delivery_sequence: u64,
pub event: InterpreterOutputEvent,
}
pub struct CanonicalEventSubscription {
pub subscriber_id: SubscriberId,
rx: mpsc::Receiver<Result<DeliveredEvent, SubscriptionStatus>>,
}
impl CanonicalEventSubscription {
pub fn new(
subscriber_id: SubscriberId,
rx: mpsc::Receiver<Result<DeliveredEvent, SubscriptionStatus>>,
) -> Self {
Self { subscriber_id, rx }
}
pub async fn recv(&mut self) -> Option<Result<DeliveredEvent, SubscriptionStatus>> {
self.rx.recv().await
}
}
#[derive(Clone)]
pub struct SubscriptionPublisher {
tx: mpsc::Sender<Result<DeliveredEvent, SubscriptionStatus>>,
next_seq: std::sync::Arc<std::sync::atomic::AtomicU64>,
}
impl SubscriptionPublisher {
pub fn channel(
subscriber_id: impl Into<String>,
capacity: usize,
) -> (Self, CanonicalEventSubscription) {
let (tx, rx) = mpsc::channel(capacity.max(1));
(
Self {
tx,
next_seq: std::sync::Arc::new(std::sync::atomic::AtomicU64::new(1)),
},
CanonicalEventSubscription::new(SubscriberId::new(subscriber_id), rx),
)
}
pub async fn publish(
&self,
event: InterpreterOutputEvent,
) -> Result<(), mpsc::error::SendError<Result<DeliveredEvent, SubscriptionStatus>>> {
let seq = self
.next_seq
.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
self.tx
.send(Ok(DeliveredEvent {
delivery_sequence: seq,
event,
}))
.await
}
pub async fn signal_gap(&self, expected: u64, observed: Option<u64>) -> Result<(), ()> {
self.tx
.send(Err(SubscriptionStatus::Gap(SubscriptionGap {
expected,
observed,
})))
.await
.map_err(|_| ())
}
pub async fn signal_lost(&self) -> Result<(), ()> {
self.tx
.send(Err(SubscriptionStatus::Lost))
.await
.map_err(|_| ())
}
}