use tokio::sync::broadcast;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ConnectionState {
Connecting,
Connected,
Reconnecting,
Degraded,
Closed,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ConnectionEvent {
Connecting,
Connected,
Reconnecting {
attempt: u32,
},
Degraded {
reason: String,
},
Closed {
error: bool,
reason: String,
},
}
impl ConnectionEvent {
pub fn state(&self) -> ConnectionState {
match self {
ConnectionEvent::Connecting => ConnectionState::Connecting,
ConnectionEvent::Connected => ConnectionState::Connected,
ConnectionEvent::Reconnecting { .. } => ConnectionState::Reconnecting,
ConnectionEvent::Degraded { .. } => ConnectionState::Degraded,
ConnectionEvent::Closed { .. } => ConnectionState::Closed,
}
}
}
#[derive(Debug, Clone)]
pub struct EventBus {
tx: broadcast::Sender<ConnectionEvent>,
}
impl EventBus {
pub fn new(capacity: usize) -> Self {
let (tx, _rx) = broadcast::channel(capacity);
EventBus { tx }
}
pub fn publish(&self, event: ConnectionEvent) {
let _ = self.tx.send(event);
}
pub fn subscribe(&self) -> broadcast::Receiver<ConnectionEvent> {
self.tx.subscribe()
}
pub fn subscriber_count(&self) -> usize {
self.tx.receiver_count()
}
}
impl Default for EventBus {
fn default() -> Self {
EventBus::new(64)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn subscribers_receive_lifecycle_sequence() {
let bus = EventBus::new(16);
let mut rx = bus.subscribe();
bus.publish(ConnectionEvent::Connecting);
bus.publish(ConnectionEvent::Connected);
bus.publish(ConnectionEvent::Reconnecting { attempt: 1 });
bus.publish(ConnectionEvent::Connected);
assert_eq!(rx.recv().await.unwrap(), ConnectionEvent::Connecting);
assert_eq!(rx.recv().await.unwrap(), ConnectionEvent::Connected);
assert_eq!(
rx.recv().await.unwrap().state(),
ConnectionState::Reconnecting
);
assert_eq!(rx.recv().await.unwrap(), ConnectionEvent::Connected);
}
}