use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
#[derive(Copy, Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ListenerId(usize);
#[derive(Debug)]
struct TypeMap {
inner: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
}
impl TypeMap {
fn set<T>(&mut self, value: T)
where
T: Any + 'static + Send + Sync,
{
self.inner.insert(TypeId::of::<T>(), Box::new(value));
}
fn has<T>(&self) -> bool
where
T: Any + 'static + Send + Sync,
{
self.inner.contains_key(&TypeId::of::<T>())
}
fn get<T>(&self) -> Option<&T>
where
T: Any + 'static + Send + Sync,
{
self.inner
.get(&TypeId::of::<T>())
.map(|t| t.downcast_ref::<T>().unwrap())
}
fn get_mut<T>(&mut self) -> Option<&mut T>
where
T: Any + 'static + Send + Sync,
{
self.inner
.get_mut(&TypeId::of::<T>())
.map(|t| t.downcast_mut::<T>().unwrap())
}
}
pub trait EventPublisher {
fn subscribe<E>(&mut self, listener: impl Fn(E) + 'static + Send + Sync) -> Option<ListenerId>
where
E: 'static;
fn unsubscribe<E>(&mut self, identifier: ListenerId)
where
E: 'static;
fn notify<E>(&self, event: E)
where
E: 'static + Clone;
fn has_listeners<E>(&self) -> bool
where
E: 'static;
}
type Subscriber<E> = dyn Fn(E) + 'static + Send + Sync;
type ListenerVec<E> = Vec<Box<Subscriber<E>>>;
#[derive(Debug)]
pub struct Publisher {
registry: TypeMap,
}
impl Default for Publisher {
fn default() -> Self {
Self {
registry: TypeMap {
inner: HashMap::new(),
},
}
}
}
impl Publisher {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
impl EventPublisher for Publisher {
fn subscribe<E>(&mut self, listener: impl Fn(E) + 'static + Send + Sync) -> Option<ListenerId>
where
E: 'static,
{
if !self.registry.has::<ListenerVec<E>>() {
self.registry.set::<ListenerVec<E>>(Vec::new());
}
let listeners = self.registry.get_mut::<ListenerVec<E>>().unwrap();
listeners.push(Box::new(listener));
Some(ListenerId(listeners.len() - 1))
}
fn unsubscribe<E>(&mut self, identifier: ListenerId)
where
E: 'static,
{
if let Some(listeners) = self.registry.get_mut::<ListenerVec<E>>() {
let _listener = listeners.remove(identifier.0);
if listeners.is_empty() {
self.registry.inner.remove(&TypeId::of::<ListenerVec<E>>());
}
}
}
#[inline]
fn notify<E>(&self, event: E)
where
E: 'static + Clone,
{
if let Some(listeners) = self.registry.get::<ListenerVec<E>>() {
for callback in listeners {
callback(event.clone());
}
}
}
#[inline]
fn has_listeners<E>(&self) -> bool
where
E: 'static,
{
self.registry
.get::<ListenerVec<E>>()
.is_some_and(|listeners| !listeners.is_empty())
}
}
impl EventPublisher for Arc<RwLock<Publisher>> {
fn subscribe<E>(&mut self, listener: impl Fn(E) + 'static + Send + Sync) -> Option<ListenerId>
where
E: 'static,
{
if let Ok(mut guard) = self.write() {
return guard.subscribe(listener);
}
None
}
fn unsubscribe<E>(&mut self, identifier: ListenerId)
where
E: 'static,
{
if let Ok(mut guard) = self.write() {
guard.unsubscribe::<E>(identifier);
}
}
#[inline]
fn notify<E>(&self, event: E)
where
E: 'static + Clone,
{
if let Ok(guard) = self.read() {
guard.notify(event);
}
}
#[inline]
fn has_listeners<E>(&self) -> bool
where
E: 'static,
{
self.read().is_ok_and(|guard| guard.has_listeners::<E>())
}
}
#[cfg(test)]
mod tests {
use crate::events::FuzzNTimes;
use super::*;
#[test]
fn test_publisher() {
fn test_fn(event: FuzzNTimes) {
assert_eq!(event.iterations, 10);
}
let mut publisher = Publisher::new();
let listener_id = publisher.subscribe(test_fn).unwrap();
assert!(publisher.registry.has::<ListenerVec<FuzzNTimes>>());
assert!(publisher
.registry
.get::<ListenerVec<FuzzNTimes>>()
.is_some());
assert!(publisher
.registry
.get_mut::<ListenerVec<FuzzNTimes>>()
.is_some());
publisher.notify(FuzzNTimes { iterations: 10 });
publisher.unsubscribe::<FuzzNTimes>(listener_id);
assert!(publisher
.registry
.get::<ListenerVec<FuzzNTimes>>()
.is_none());
assert!(!publisher.has_listeners::<FuzzNTimes>());
}
}