use std::{
fmt,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Weak,
},
};
use crossbeam_skiplist::SkipSet;
pub trait Event: fmt::Debug + Send + Sync {
type HandlerReturnType: fmt::Debug;
fn update(&mut self, _handler_result: Self::HandlerReturnType) {}
}
#[derive(Debug)]
struct ListenerEntry<E: Event> {
callback: Weak<dyn Fn(&E) -> E::HandlerReturnType + Send + Sync>,
order: usize,
}
impl<E: Event> Eq for ListenerEntry<E> {}
impl<E: Event> PartialEq for ListenerEntry<E> {
fn eq(&self, other: &Self) -> bool {
self.order == other.order
}
}
impl<E: Event> Ord for ListenerEntry<E> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.order.cmp(&other.order)
}
}
impl<E: Event> PartialOrd for ListenerEntry<E> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
static LISTENER_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
pub struct ListenerList<E: Event> {
inner: SkipSet<ListenerEntry<E>>,
}
impl<E: Event + 'static> ListenerList<E> {
pub fn new() -> Self {
ListenerList {
inner: SkipSet::new(),
}
}
pub(crate) fn dispatch(&self, event: &mut E) {
let mut entries_to_remove = Vec::new();
for entry in self.inner.iter() {
if let Some(callback_arc) = entry.callback.upgrade() {
let result = callback_arc(event);
event.update(result);
} else {
entries_to_remove.push(entry.order);
}
}
let dummy_arc: Arc::<dyn Fn(&E) -> E::HandlerReturnType + Send + Sync> = Arc::new(Self::dummy_handler);
for order in entries_to_remove {
let key = ListenerEntry {
callback: Arc::downgrade(&dummy_arc), order,
};
self.inner.remove(&key);
}
}
fn dummy_handler(_: &E) -> E::HandlerReturnType {
unreachable!()
}
}
impl<E: Event + 'static> Default for ListenerList<E> {
fn default() -> Self {
Self::new()
}
}
impl<E: Event> fmt::Debug for ListenerList<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ListenerList")
.field("listener_count", &self.inner.len())
.finish()
}
}
pub struct Listener<E: Event> {
#[allow(dead_code)] arc: Arc<dyn Fn(&E) -> E::HandlerReturnType + Send + Sync>,
order: usize,
}
impl<E: Event + 'static> Listener<E> {
pub fn new<F>(listeners: &ListenerList<E>, callback: F) -> Self
where
F: Fn(&E) -> E::HandlerReturnType + Send + Sync + 'static,
{
let order = LISTENER_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
let arc: Arc<dyn Fn(&E) -> E::HandlerReturnType + Send + Sync> = Arc::new(callback);
let entry = ListenerEntry {
callback: Arc::downgrade(&arc),
order,
};
listeners.inner.insert(entry);
Listener { arc, order }
}
}
impl<E: Event> fmt::Debug for Listener<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Listener")
.field("order", &self.order)
.finish()
}
}
macro_rules! define_event_listeners {
($struct_name:ident { $($field_name:ident: $event_type:ty),* $(,)? }) => {
#[derive(Debug, Default)]
pub struct $struct_name {
$(
pub $field_name: $crate::event::ListenerList<$event_type>,
)*
}
impl $struct_name {
pub fn new() -> Self {
Self {
$(
$field_name: $crate::event::ListenerList::new(),
)*
}
}
}
};
}
pub(crate) use define_event_listeners;
#[cfg(test)]
mod tests {
use std::{cell::RefCell, sync::Mutex};
use super::*;
#[derive(Debug, Clone)]
pub struct Event1(i32);
impl Event for Event1 {
type HandlerReturnType = i32;
fn update(&mut self, value: i32) {
self.0 += value;
println!("Event1 updated: current value = {}", self.0); }
}
#[derive(Debug, Clone)]
pub struct Event2(i32);
impl Event for Event2 {
type HandlerReturnType = i32;
fn update(&mut self, value: i32) {
self.0 *= value; println!("Event2 updated: current value = {}", self.0); }
}
define_event_listeners!(TestEvents {
on_event1: Event1,
on_event2: Event2,
});
#[test]
fn test_listener_registration_and_dispatch() {
let listeners = TestEvents::new();
let _listener1_handle = Listener::new(&listeners.on_event1, |event| {
println!("Listener 1 for Event1 called with event: {:?}", event);
10 });
let _listener2_handle = Listener::new(&listeners.on_event1, |event| {
println!("Listener 2 for Event1 called with event: {:?}", event);
5 });
let _listener3_handle = Listener::new(&listeners.on_event2, |event| {
println!("Listener 1 for Event2 called with event: {:?}", event);
3 });
let mut event1 = Event1(100); println!("Dispatching Event1...");
listeners.on_event1.dispatch(&mut event1);
assert_eq!(event1.0, 115);
println!("Event1 dispatch complete. Final value: {}", event1.0);
let mut event2 = Event2(5); println!("Dispatching Event2...");
listeners.on_event2.dispatch(&mut event2);
assert_eq!(event2.0, 15);
println!("Event2 dispatch complete. Final value: {}", event2.0);
}
#[test]
fn test_listener_cleanup_on_drop() {
let listeners = TestEvents::new();
let listener_count_before: usize;
let listener_count_after_drop: usize;
let listener_count_after_dispatch: usize;
{
let _listener_temp = Listener::new(&listeners.on_event1, |_| 1);
listener_count_before = listeners.on_event1.inner.len();
println!("Listener count before drop: {}", listener_count_before);
assert_eq!(listener_count_before, 1);
}
listener_count_after_drop = listeners.on_event1.inner.len();
println!("Listener count after drop (before dispatch): {}", listener_count_after_drop);
assert_eq!(listener_count_after_drop, 1);
let mut event1 = Event1(0);
println!("Dispatching event to trigger cleanup...");
listeners.on_event1.dispatch(&mut event1);
listener_count_after_dispatch = listeners.on_event1.inner.len();
println!("Listener count after dispatch (should be 0): {}", listener_count_after_dispatch);
assert_eq!(listener_count_after_dispatch, 0);
assert_eq!(event1.0, 0);
}
#[test]
fn test_listener_order() {
let listeners = TestEvents::new();
let call_order = Arc::new(Mutex::new(Vec::new()));
let (co_a, co_b, co_c) = (call_order.clone(), call_order.clone(), call_order.clone());
let _listener_a = Listener::new(&listeners.on_event1, move |_| {
println!("Listener A called");
co_a.try_lock().unwrap().push("A");
1 });
let _listener_b = Listener::new(&listeners.on_event1, move |_| {
println!("Listener B called");
co_b.try_lock().unwrap().push("B");
1
});
let _listener_c = Listener::new(&listeners.on_event1, move |_| {
println!("Listener C called");
co_c.try_lock().unwrap().push("C");
1
});
let mut event1 = Event1(0);
println!("Dispatching event to test order...");
listeners.on_event1.dispatch(&mut event1);
println!("Call order: {:?}", call_order);
assert_eq!(std::mem::take(&mut *call_order.try_lock().unwrap()), vec!["A", "B", "C"]);
}
}