use super::{WeakEventArgs, WeakEventHandler, WeakEventSender};
use std::any::{Any, TypeId};
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::ops::Deref;
use std::rc::{Rc, Weak};
pub trait WeakEventHandlerTarget<A: WeakEventArgs + ?Sized> {
fn add_event_handler(&self, event_name: &str, handler: Rc<WeakEventHandler<A>>) -> Option<Box<dyn FnOnce()>>;
}
pub type WeakEventHandlerMethod<A, TSubscriber> =
for<'s, 'r, 'a> fn(&TSubscriber, Option<&'s dyn Any>, &'r <A as WeakEventArgs>::Args<'a>);
pub struct WeakEventHandlerManager;
impl WeakEventHandlerManager {
pub fn subscribe<TTarget, A, TSubscriber>(
target: &TTarget,
event_name: &str,
subscriber: &Rc<TSubscriber>,
method: WeakEventHandlerMethod<A, TSubscriber>,
) where
TTarget: WeakEventSender + Deref,
TTarget::Target: WeakEventHandlerTarget<A>,
A: WeakEventArgs + ?Sized,
TSubscriber: 'static,
{
let dic = SubscriptionTypeStorage::<A, TSubscriber>::subscribers().get_or_create_value(target);
let existing = dic.subscriptions.borrow().get(event_name).cloned();
let sub = match existing {
Some(sub) => sub,
None => {
let sub = Subscription::new(&dic, target, event_name);
dic.subscriptions.borrow_mut().insert(event_name.to_string(), sub.clone());
sub
}
};
sub.add(subscriber, method);
}
pub fn unsubscribe<A, TSubscriber>(target: &impl WeakEventSender, event_name: &str, subscriber: &Rc<TSubscriber>)
where
A: WeakEventArgs + ?Sized,
TSubscriber: 'static,
{
if let Some(dic) = SubscriptionTypeStorage::<A, TSubscriber>::subscribers().try_get_value(target) {
let sub = dic.subscriptions.borrow().get(event_name).cloned();
if let Some(sub) = sub {
sub.remove(subscriber);
}
}
}
}
crate::ferro_thread_local! {
static SUBSCRIPTION_TYPE_STORAGE: RefCell<HashMap<TypeId, Box<dyn Any>>> = RefCell::new(HashMap::new());
}
struct SubscriptionTypeStorage<A: WeakEventArgs + ?Sized, TSubscriber: 'static> {
subscribers: RefCell<HashMap<usize, Rc<SubscriptionDic<A, TSubscriber>>>>,
sweep_at: Cell<usize>,
}
const MIN_SWEEP: usize = 16;
impl<A: WeakEventArgs + ?Sized, TSubscriber: 'static> SubscriptionTypeStorage<A, TSubscriber> {
fn subscribers() -> Rc<Self> {
SUBSCRIPTION_TYPE_STORAGE.with(|storage| {
storage
.borrow_mut()
.entry(TypeId::of::<fn(&A) -> TSubscriber>())
.or_insert_with(|| {
Box::new(Rc::new(Self { subscribers: RefCell::new(HashMap::new()), sweep_at: Cell::new(MIN_SWEEP) }))
})
.downcast_ref::<Rc<Self>>()
.expect("the storage of a type pair holds that pair")
.clone()
})
}
fn try_get_value(&self, target: &impl WeakEventSender) -> Option<Rc<SubscriptionDic<A, TSubscriber>>> {
self.subscribers.borrow().get(&target.sender_address()).cloned()
}
fn get_or_create_value<TTarget: WeakEventSender>(&self, target: &TTarget) -> Rc<SubscriptionDic<A, TSubscriber>> {
if let Some(dic) = self.try_get_value(target) {
return dic;
}
let weak_target = target.downgrade_sender();
let dic = Rc::new(SubscriptionDic {
subscriptions: RefCell::new(HashMap::new()),
target_alive: Box::new(move || TTarget::upgrade_sender(&weak_target).is_some()),
});
let mut subscribers = self.subscribers.borrow_mut();
subscribers.insert(target.sender_address(), dic.clone());
if subscribers.len() >= self.sweep_at.get() {
subscribers.retain(|_, dic| (dic.target_alive)());
self.sweep_at.set((subscribers.len() * 2).max(MIN_SWEEP));
}
dic
}
}
struct SubscriptionDic<A: WeakEventArgs + ?Sized, TSubscriber: 'static> {
subscriptions: RefCell<HashMap<String, Rc<Subscription<A, TSubscriber>>>>,
target_alive: Box<dyn Fn() -> bool>,
}
struct Descriptor<A: WeakEventArgs + ?Sized, TSubscriber> {
subscriber: Option<Weak<TSubscriber>>,
caller: Option<WeakEventHandlerMethod<A, TSubscriber>>,
}
impl<A: WeakEventArgs + ?Sized, TSubscriber> Default for Descriptor<A, TSubscriber> {
fn default() -> Self {
Self { subscriber: None, caller: None }
}
}
impl<A: WeakEventArgs + ?Sized, TSubscriber> Clone for Descriptor<A, TSubscriber> {
fn clone(&self) -> Self {
Self { subscriber: self.subscriber.clone(), caller: self.caller }
}
}
struct Subscription<A: WeakEventArgs + ?Sized, TSubscriber: 'static> {
sdic: Weak<SubscriptionDic<A, TSubscriber>>,
event_name: String,
remove_handler: RefCell<Option<Box<dyn FnOnce()>>>,
data: RefCell<Vec<Descriptor<A, TSubscriber>>>,
count: Cell<usize>,
}
impl<A: WeakEventArgs + ?Sized, TSubscriber: 'static> Subscription<A, TSubscriber> {
fn new<TTarget>(sdic: &Rc<SubscriptionDic<A, TSubscriber>>, target: &TTarget, event_name: &str) -> Rc<Self>
where
TTarget: WeakEventSender + Deref,
TTarget::Target: WeakEventHandlerTarget<A>,
{
let this = Rc::new(Self {
sdic: Rc::downgrade(sdic),
event_name: event_name.to_string(),
remove_handler: RefCell::new(None),
data: RefCell::new(vec![Descriptor::default(); 2]),
count: Cell::new(0),
});
let weak = Rc::downgrade(&this);
let delegate: Rc<WeakEventHandler<A>> = Rc::new(move |sender: Option<&dyn Any>, event_args: &A::Args<'_>| {
if let Some(this) = weak.upgrade() {
this.on_event(sender, event_args);
}
});
match (**target).add_event_handler(event_name, delegate) {
Some(remove_handler) => *this.remove_handler.borrow_mut() = Some(remove_handler),
None => panic!("The event {event_name} was not found on {}.", std::any::type_name::<TTarget::Target>()),
}
this
}
fn destroy(&self) {
let remove_handler = self.remove_handler.borrow_mut().take();
if let Some(remove_handler) = remove_handler {
remove_handler();
}
if let Some(sdic) = self.sdic.upgrade() {
sdic.subscriptions.borrow_mut().remove(&self.event_name);
}
}
fn add(&self, subscriber: &Rc<TSubscriber>, method: WeakEventHandlerMethod<A, TSubscriber>) {
self.compact(true);
let mut data = self.data.borrow_mut();
if self.count.get() == data.len() {
let len = data.len();
data.resize(len * 2, Descriptor::default());
}
data[self.count.get()] = Descriptor { caller: Some(method), subscriber: Some(Rc::downgrade(subscriber)) };
self.count.set(self.count.get() + 1);
}
fn remove(&self, subscriber: &Rc<TSubscriber>) {
let mut removed = false;
{
let mut data = self.data.borrow_mut();
for c in 0..self.count.get() {
let is_subscriber = data[c]
.subscriber
.as_ref()
.and_then(Weak::upgrade)
.is_some_and(|instance| Rc::ptr_eq(&instance, subscriber));
if is_subscriber {
data[c] = Descriptor::default();
removed = true;
}
}
}
if removed {
self.compact(false);
}
}
fn compact(&self, prevent_destroy: bool) {
let mut empty: Option<usize> = None;
{
let mut data = self.data.borrow_mut();
for c in 0..self.count.get() {
let r = data[c].clone();
let target = r.subscriber.as_ref().and_then(Weak::upgrade);
if target.is_none() && empty.is_none() {
empty = Some(c);
}
if let (Some(_), Some(e)) = (&target, empty) {
data[c] = Descriptor::default();
data[e] = r;
empty = Some(e + 1);
}
}
}
if let Some(empty) = empty {
self.count.set(empty);
}
if self.count.get() == 0 && !prevent_destroy {
self.destroy();
}
}
fn on_event(&self, sender: Option<&dyn Any>, event_args: &A::Args<'_>) {
let mut need_compact = false;
let mut c = 0;
while c < self.count.get() {
let descriptor = self.data.borrow()[c].clone();
let r = descriptor.subscriber.expect("a counted descriptor has a subscriber");
match r.upgrade() {
Some(sub) => (descriptor.caller.expect("a counted descriptor has a caller"))(&sub, sender, event_args),
None => need_compact = true,
}
c += 1;
}
if need_compact {
self.compact(false);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utilities::EventArgs;
struct EventSource {
event: RefCell<Vec<Rc<WeakEventHandler<EventArgs>>>>,
this: Weak<EventSource>,
}
impl EventSource {
fn new() -> Rc<Self> {
Rc::new_cyclic(|this| Self { event: RefCell::new(Vec::new()), this: this.clone() })
}
fn fire(&self) {
let this = self.this.upgrade();
let handlers = self.event.borrow().clone();
for handler in handlers {
handler(this.as_ref().map(|this| this as &dyn Any), &EventArgs::EMPTY);
}
}
}
impl WeakEventHandlerTarget<EventArgs> for EventSource {
fn add_event_handler(
&self,
event_name: &str,
handler: Rc<WeakEventHandler<EventArgs>>,
) -> Option<Box<dyn FnOnce()>> {
if event_name != "Event" {
return None;
}
self.event.borrow_mut().push(handler.clone());
let this = self.this.clone();
Some(Box::new(move || {
if let Some(this) = this.upgrade() {
let mut event = this.event.borrow_mut();
if let Some(index) = event.iter().rposition(|h| Rc::ptr_eq(h, &handler)) {
event.remove(index);
}
}
}))
}
}
struct Subscriber {
on_event: Option<Box<dyn Fn()>>,
}
impl Subscriber {
fn new(on_event: Box<dyn Fn()>) -> Rc<Self> {
Rc::new(Self { on_event: Some(on_event) })
}
fn on_event(&self, _sender: Option<&dyn Any>, _ev: &EventArgs) {
if let Some(on_event) = &self.on_event {
on_event();
}
}
}
#[test]
fn event_should_be_passed_to_subscriber() {
let handled = Rc::new(Cell::new(false));
let h = handled.clone();
let subscriber = Subscriber::new(Box::new(move || h.set(true)));
let source = EventSource::new();
WeakEventHandlerManager::subscribe::<_, EventArgs, Subscriber>(&source, "Event", &subscriber, Subscriber::on_event);
source.fire();
assert!(handled.get());
}
#[test]
fn event_should_not_be_raised_after_unsubscribe() {
let handled = Rc::new(Cell::new(false));
let h = handled.clone();
let subscriber = Subscriber::new(Box::new(move || h.set(true)));
let source = EventSource::new();
WeakEventHandlerManager::subscribe::<_, EventArgs, Subscriber>(&source, "Event", &subscriber, Subscriber::on_event);
WeakEventHandlerManager::unsubscribe::<EventArgs, Subscriber>(&source, "Event", &subscriber);
source.fire();
assert!(!handled.get());
}
#[test]
fn event_handler_should_not_be_kept_alive() {
let handled = Rc::new(Cell::new(false));
let source = EventSource::new();
let h = handled.clone();
add_collectable_subscriber(&source, "Event", Box::new(move || h.set(true)));
source.fire();
assert!(!handled.get());
}
fn add_collectable_subscriber(source: &Rc<EventSource>, name: &str, func: Box<dyn Fn()>) {
WeakEventHandlerManager::subscribe::<_, EventArgs, Subscriber>(source, name, &Subscriber::new(func), Subscriber::on_event);
}
}