use core::cell::RefCell;
use std::sync::{Arc, OnceLock};
use crate::Message;
static SUBSCRIBERS: OnceLock<Subscribers> = OnceLock::new();
thread_local! {
static SCOPED: RefCell<Option<Arc<Subscribers>>> = const { RefCell::new(None) };
}
#[must_use = "dropping the guard immediately ends the scope"]
pub struct DefaultGuard(Option<Arc<Subscribers>>);
pub trait Subscriber: Send + Sync {
fn on_message(&self, msg: &dyn Message);
}
#[derive(Default)]
pub struct Subscribers(Vec<Box<dyn Subscriber>>);
impl Subscribers {
fn dispatch(&self, msg: &dyn Message) {
for sub in &self.0 {
sub.on_message(msg);
}
}
pub fn install(self) -> Result<(), Subscribers> {
SUBSCRIBERS.set(self)
}
pub fn register(&mut self, subscriber: Box<dyn Subscriber>) {
self.0.push(subscriber);
}
pub fn set_default(self) -> DefaultGuard {
let previous = SCOPED.with(|cell| cell.borrow_mut().replace(Arc::new(self)));
DefaultGuard(previous)
}
pub fn with_default<T>(self, f: impl FnOnce() -> T) -> T {
let _guard = self.set_default();
f()
}
}
impl Drop for DefaultGuard {
fn drop(&mut self) {
SCOPED.with(|cell| {
*cell.borrow_mut() = self.0.take();
});
}
}
pub(crate) fn dispatch(msg: &dyn Message) {
if let Some(subscribers) = SCOPED
.with(|cell| cell.borrow().clone())
.as_deref()
.or_else(|| SUBSCRIBERS.get())
{
subscribers.dispatch(msg);
}
}