use std::sync::Arc;
pub trait Event: Clone {}
pub type EventListener<T, Context> = Arc<dyn Fn(&T, &mut Context) + Send + Sync>;
pub struct EventEmitter<T: Event, Context> {
listeners: Vec<EventListener<T, Context>>,
}
impl<T: Event, Context> std::fmt::Debug for EventEmitter<T, Context> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EventEmitter")
.field("listener_count", &self.listeners.len())
.finish()
}
}
impl<T: Event, Context> EventEmitter<T, Context> {
pub fn new() -> Self {
Self {
listeners: Vec::new(),
}
}
pub fn subscribe<F>(&mut self, listener: F)
where
F: Fn(&T, &mut Context) + Send + Sync + 'static,
{
self.listeners.push(Arc::new(listener));
}
pub fn emit(self, event: T, context: &mut Context) -> Self {
let listeners: Vec<_> = self.listeners.iter().cloned().collect();
for listener in listeners {
listener(&event, context);
}
self
}
pub fn listener_count(&self) -> usize {
self.listeners.len()
}
pub fn clear_listeners(&mut self) {
self.listeners.clear();
}
}
impl<T: Event, Context> Default for EventEmitter<T, Context> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, PartialEq)]
enum TestEvent {
ValueChanged { old: i32, new: i32 },
Reset,
}
impl Event for TestEvent {}
struct TestContext {
value: i32,
event_log: Vec<String>,
emitter: EventEmitter<TestEvent, TestContext>,
}
impl TestContext {
fn new() -> Self {
Self {
value: 0,
event_log: Vec::new(),
emitter: EventEmitter::new(),
}
}
fn set_value(&mut self, new_value: i32) {
let old = self.value;
self.value = new_value;
let emitter = std::mem::take(&mut self.emitter);
self.emitter = emitter.emit(
TestEvent::ValueChanged {
old,
new: new_value,
},
self,
);
}
fn reset(&mut self) {
self.value = 0;
let emitter = std::mem::take(&mut self.emitter);
self.emitter = emitter.emit(TestEvent::Reset, self);
}
}
#[test]
fn test_event_emitter_creation() {
let emitter: EventEmitter<TestEvent, TestContext> = EventEmitter::new();
assert_eq!(emitter.listener_count(), 0);
}
#[test]
fn test_subscribe_and_emit() {
let mut context = TestContext::new();
context.emitter.subscribe(|event, ctx| {
ctx.event_log.push(format!("Event received: {event:?}"));
});
assert_eq!(context.emitter.listener_count(), 1);
context.set_value(42);
assert_eq!(context.event_log.len(), 1);
assert!(context.event_log[0].contains("ValueChanged"));
}
#[test]
fn test_multiple_listeners() {
let mut context = TestContext::new();
context.emitter.subscribe(|_event, ctx| {
ctx.event_log.push("Listener 1".to_string());
});
context.emitter.subscribe(|_event, ctx| {
ctx.event_log.push("Listener 2".to_string());
});
assert_eq!(context.emitter.listener_count(), 2);
context.set_value(100);
assert_eq!(context.event_log.len(), 2);
assert_eq!(context.event_log[0], "Listener 1");
assert_eq!(context.event_log[1], "Listener 2");
}
#[test]
fn test_clear_listeners() {
let mut context = TestContext::new();
context.emitter.subscribe(|_event, ctx| {
ctx.event_log.push("Should not be called".to_string());
});
context.emitter.clear_listeners();
assert_eq!(context.emitter.listener_count(), 0);
context.set_value(50);
assert_eq!(context.event_log.len(), 0);
}
#[test]
fn test_different_event_types() {
let mut context = TestContext::new();
context.emitter.subscribe(|event, ctx| match event {
TestEvent::ValueChanged { old, new } => {
ctx.event_log.push(format!("Changed from {old} to {new}"));
}
TestEvent::Reset => {
ctx.event_log.push("Reset".to_string());
}
});
context.set_value(10);
context.set_value(20);
context.reset();
assert_eq!(context.event_log.len(), 3);
assert_eq!(context.event_log[0], "Changed from 0 to 10");
assert_eq!(context.event_log[1], "Changed from 10 to 20");
assert_eq!(context.event_log[2], "Reset");
}
}