use serde::Serialize;
use tokio::sync::broadcast;
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum PanelEvent {
ToolStarted { tool: String },
ToolDone { tool: String, duration_ms: u64 },
ToolFailed { tool: String, error: String },
MessageReceived { channel: String, chat_id: String },
AgentStarted { session_key: String },
AgentDone { session_key: String, tokens: u64 },
Compaction { from_tokens: u64, to_tokens: u64 },
ChannelStatus { channel: String, status: String },
CronFired { job_id: String, status: String },
}
#[derive(Clone)]
pub struct EventBus {
tx: broadcast::Sender<PanelEvent>,
}
impl EventBus {
pub fn new(capacity: usize) -> Self {
let (tx, _) = broadcast::channel(capacity);
Self { tx }
}
pub fn send(&self, event: PanelEvent) {
let _ = self.tx.send(event);
}
pub fn subscribe(&self) -> broadcast::Receiver<PanelEvent> {
self.tx.subscribe()
}
pub fn receiver_count(&self) -> usize {
self.tx.receiver_count()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_event_bus_send_receive() {
let bus = EventBus::new(16);
let mut rx = bus.subscribe();
bus.send(PanelEvent::ChannelStatus {
channel: "telegram".into(),
status: "up".into(),
});
let event = rx.recv().await.unwrap();
match event {
PanelEvent::ChannelStatus { channel, status } => {
assert_eq!(channel, "telegram");
assert_eq!(status, "up");
}
_ => panic!("unexpected event type"),
}
}
#[tokio::test]
async fn test_event_bus_multiple_subscribers() {
let bus = EventBus::new(16);
let mut rx1 = bus.subscribe();
let mut rx2 = bus.subscribe();
bus.send(PanelEvent::AgentStarted {
session_key: "test:123".into(),
});
assert!(rx1.recv().await.is_ok());
assert!(rx2.recv().await.is_ok());
}
#[tokio::test]
async fn test_event_bus_no_subscribers_no_panic() {
let bus = EventBus::new(16);
bus.send(PanelEvent::ToolStarted {
tool: "echo".into(),
});
}
#[tokio::test]
async fn test_event_bus_receiver_count() {
let bus = EventBus::new(16);
assert_eq!(bus.receiver_count(), 0);
let _rx1 = bus.subscribe();
assert_eq!(bus.receiver_count(), 1);
let _rx2 = bus.subscribe();
assert_eq!(bus.receiver_count(), 2);
}
#[test]
fn test_panel_event_serialization() {
let event = PanelEvent::ToolDone {
tool: "web_search".into(),
duration_ms: 230,
};
let json = serde_json::to_string(&event).unwrap();
assert!(json.contains(r#""type":"tool_done""#));
assert!(json.contains(r#""tool":"web_search""#));
assert!(json.contains(r#""duration_ms":230"#));
}
}