use std::{
cell::{Cell, RefCell},
collections::HashMap,
hash::Hash,
};
#[derive(Debug, PartialEq)]
pub enum Error {
BusLock,
Disconnected,
}
pub trait EventEmitter<E, V> {
fn on<F>(&self, event: E, f: F) -> Result<(), Error>
where
F: Fn(&BusRef<E, V>, Option<&V>) + 'static;
fn emit_with_value(&self, event: E, value: Option<&V>) -> Result<(), Error>;
fn emit(&self, event: E) -> Result<(), Error> {
self.emit_with_value(event, None)
}
}
pub struct BusRef<E, V> {
marker: std::marker::PhantomData<E>,
listeners: RefCell<HashMap<E, Vec<Box<dyn Fn(&Self, Option<&V>)>>>>,
emit_count: Cell<usize>,
emit_limit: usize,
}
impl<E, V> BusRef<E, V> {
pub(crate) fn unbound() -> Self {
Self {
marker: std::marker::PhantomData,
listeners: RefCell::new(HashMap::new()),
emit_count: Cell::new(0),
emit_limit: 0,
}
}
pub(crate) fn bound(max_emit_count: usize) -> Self {
Self {
marker: std::marker::PhantomData,
listeners: RefCell::new(HashMap::new()),
emit_count: Cell::new(0),
emit_limit: max_emit_count,
}
}
pub fn disconnected(&self) -> bool {
let event_count = self.event_count();
event_count != 0 && event_count == self.emit_limit
}
pub fn event_count(&self) -> usize {
self.emit_count.get()
}
}
impl<E, V> EventEmitter<E, V> for BusRef<E, V>
where
E: Hash + Eq,
{
fn on<F>(&self, event: E, f: F) -> Result<(), Error>
where
F: Fn(&Self, Option<&V>) + 'static,
{
let boxed_fn = Box::new(f);
match self.listeners.try_borrow_mut() {
Ok(mut listeners) => {
match listeners.get_mut(&event) {
Some(existing_event) => {
existing_event.push(boxed_fn);
}
None => {
let v: Vec<Box<dyn Fn(&Self, Option<&V>) + 'static>> = vec![boxed_fn];
listeners.insert(event, v);
}
}
Ok(())
},
Err(_) => Err(Error::BusLock)
}
}
fn emit(&self, event: E) -> Result<(), Error> {
self.emit_with_value(event, None)
}
fn emit_with_value(&self, event: E, value: Option<&V>) -> Result<(), Error> {
if self.disconnected() {
Err(Error::Disconnected)
} else {
let event_count = self.emit_count.get();
self.emit_count.set(event_count + 1);
let listeners = self.listeners.borrow();
match listeners.get(&event) {
Some(listeners_fns) => {
let _results = listeners_fns.iter().map(|l| l(&self, value)).collect::<()>();
Ok(())
}
None => Ok(()),
}
}
}
}