use crate::events::{Event, EventConfig};
use log::debug;
use std::sync::Arc;
use tokio::sync::broadcast;
#[derive(Clone)]
pub struct EventEmitter {
sender: broadcast::Sender<Event>,
config: Arc<EventConfig>,
}
impl EventEmitter {
pub fn new(config: EventConfig) -> Self {
let config = Arc::new(config);
let sender = broadcast::Sender::new(config.channel_capacity);
debug!(
"EventEmitter created with channel_capacity={}",
config.channel_capacity
);
Self { sender, config }
}
pub fn with_default_config() -> Self {
Self::new(EventConfig::default())
}
pub fn builder() -> EventEmitterBuilder {
EventEmitterBuilder::new()
}
pub async fn emit(&self, event: Event) -> Result<usize, broadcast::error::SendError<Event>> {
if !self.config.enabled {
debug!("Event system is disabled, dropping event: {}", event.name());
return Ok(0);
}
if self.config.event_logging_enabled {
debug!(
"Emitting event: name={}, severity={}",
event.name(),
event.severity()
);
}
let receiver_count = self.sender.send(event)?;
if self.config.event_logging_enabled {
debug!("Event emitted to {} receivers", receiver_count);
}
Ok(receiver_count)
}
pub fn subscribe(&self) -> broadcast::Receiver<Event> {
let receiver = self.sender.subscribe();
debug!("New event subscriber created");
receiver
}
pub fn receiver_count(&self) -> usize {
self.sender.receiver_count()
}
pub fn config(&self) -> &EventConfig {
&self.config
}
pub fn is_enabled(&self) -> bool {
self.config.enabled
}
}
pub struct EventEmitterBuilder {
config: EventConfig,
}
impl EventEmitterBuilder {
pub fn new() -> Self {
Self {
config: EventConfig::default(),
}
}
pub fn with_channel_capacity(mut self, capacity: usize) -> Self {
self.config.channel_capacity = capacity;
self
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.config.enabled = enabled;
self
}
pub fn with_webhook_urls(mut self, urls: Vec<String>) -> Self {
self.config.webhook_enabled = !urls.is_empty();
self.config.webhook_urls = urls;
self
}
pub fn with_logging(mut self, enabled: bool) -> Self {
self.config.event_logging_enabled = enabled;
self
}
pub fn build(self) -> EventEmitter {
EventEmitter::new(self.config)
}
}
impl Default for EventEmitterBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::events::EventType;
#[tokio::test]
async fn test_event_emitter_emit_and_receive() {
let emitter = EventEmitter::with_default_config();
let mut receiver = emitter.subscribe();
let event = Event::new(EventType::RateLimitTriggered {
key: "test_key".to_string(),
rule_id: "test_rule".to_string(),
decision: "Deny".to_string(),
});
let result = emitter.emit(event.clone()).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), 1);
let received = receiver.recv().await.unwrap();
assert_eq!(received.name(), event.name());
}
#[tokio::test]
async fn test_event_emitter_multiple_subscribers() {
let emitter = EventEmitter::with_default_config();
let mut receiver1 = emitter.subscribe();
let mut receiver2 = emitter.subscribe();
let event = Event::new(EventType::BanApplied {
target: "user123".to_string(),
reason: "Test".to_string(),
duration: 60,
});
let result = emitter.emit(event).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), 2);
let received1 = receiver1.recv().await.unwrap();
let received2 = receiver2.recv().await.unwrap();
assert_eq!(received1.name(), received2.name());
}
#[tokio::test]
async fn test_event_emitter_disabled() {
let config = EventConfig {
enabled: false,
..EventConfig::default()
};
let emitter = EventEmitter::new(config);
let event = Event::new(EventType::CircuitStateChanged {
from: "Closed".to_string(),
to: "Open".to_string(),
});
let result = emitter.emit(event).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), 0);
}
#[tokio::test]
async fn test_event_emitter_receiver_count() {
let emitter = EventEmitter::with_default_config();
assert_eq!(emitter.receiver_count(), 0);
let _receiver1 = emitter.subscribe();
assert_eq!(emitter.receiver_count(), 1);
let _receiver2 = emitter.subscribe();
assert_eq!(emitter.receiver_count(), 2);
}
#[tokio::test]
async fn test_event_emitter_clone() {
let emitter1 = EventEmitter::with_default_config();
let mut receiver = emitter1.subscribe();
let emitter2 = emitter1.clone();
let event = Event::new(EventType::QuotaAlert {
user_id: "user1".to_string(),
resource: "api_calls".to_string(),
usage_percent: 85.0,
});
let result = emitter2.emit(event).await;
assert!(result.is_ok());
let received = receiver.recv().await.unwrap();
assert_eq!(received.name(), "quota_alert");
}
#[test]
fn test_event_emitter_builder() {
let emitter = EventEmitterBuilder::new()
.with_channel_capacity(2048)
.enabled(true)
.with_logging(false)
.build();
assert!(emitter.is_enabled());
assert_eq!(emitter.config().channel_capacity, 2048);
assert!(!emitter.config().event_logging_enabled);
}
#[tokio::test]
async fn test_event_emitter_with_metadata() {
use ahash::AHashMap as StdHashMap;
let emitter = EventEmitter::with_default_config();
let mut receiver = emitter.subscribe();
let mut metadata = StdHashMap::new();
metadata.insert("request_id".to_string(), serde_json::json!("req_123"));
let event = Event::with_metadata(
EventType::RateLimitTriggered {
key: "key".to_string(),
rule_id: "rule".to_string(),
decision: "Deny".to_string(),
},
metadata,
);
emitter.emit(event).await.unwrap();
let received = receiver.recv().await.unwrap();
assert_eq!(
received.metadata.get("request_id"),
Some(&serde_json::json!("req_123"))
);
}
}