use std::any::Any;
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, RwLock};
pub type ReceiverFuture = Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ReceiverId(u64);
#[derive(Debug, Clone)]
pub struct AdminSaveContext {
pub table: &'static str,
pub pk: String,
pub change: bool,
}
#[derive(Debug, Clone)]
pub struct AdminDeleteContext {
pub table: &'static str,
pub pk: String,
}
type Receiver<C> = Arc<dyn Fn(C) -> ReceiverFuture + Send + Sync + 'static>;
struct Channel<C>(RwLock<Vec<(ReceiverId, Receiver<C>)>>);
impl<C: Clone + Send + 'static> Channel<C> {
const fn new() -> Self {
Self(RwLock::new(Vec::new()))
}
fn connect<F, Fut>(&self, receiver: F) -> ReceiverId
where
F: Fn(C) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ()> + Send + 'static,
{
let id = next_id();
let wrapped: Receiver<C> =
Arc::new(move |ctx| -> ReceiverFuture { Box::pin(receiver(ctx)) });
self.0.write().unwrap().push((id, wrapped));
id
}
fn disconnect(&self, id: ReceiverId) {
self.0.write().unwrap().retain(|(rid, _)| *rid != id);
}
async fn send(&self, ctx: C) {
let receivers: Vec<Receiver<C>> = self
.0
.read()
.unwrap()
.iter()
.map(|(_, r)| r.clone())
.collect();
for r in receivers {
r(ctx.clone()).await;
}
}
fn len(&self) -> usize {
self.0.read().unwrap().len()
}
fn clear(&self) {
self.0.write().unwrap().clear();
}
}
static PRE_SAVE: Channel<AdminSaveContext> = Channel::new();
static POST_SAVE: Channel<AdminSaveContext> = Channel::new();
static PRE_DELETE: Channel<AdminDeleteContext> = Channel::new();
static POST_DELETE: Channel<AdminDeleteContext> = Channel::new();
static NEXT_ID: AtomicU64 = AtomicU64::new(1);
fn next_id() -> ReceiverId {
ReceiverId(NEXT_ID.fetch_add(1, Ordering::Relaxed))
}
pub fn connect_admin_pre_save<F, Fut>(receiver: F) -> ReceiverId
where
F: Fn(AdminSaveContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ()> + Send + 'static,
{
PRE_SAVE.connect(receiver)
}
pub fn disconnect_admin_pre_save(id: ReceiverId) {
PRE_SAVE.disconnect(id);
}
pub async fn send_admin_pre_save(ctx: AdminSaveContext) {
PRE_SAVE.send(ctx).await;
}
pub fn connect_admin_post_save<F, Fut>(receiver: F) -> ReceiverId
where
F: Fn(AdminSaveContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ()> + Send + 'static,
{
POST_SAVE.connect(receiver)
}
pub fn disconnect_admin_post_save(id: ReceiverId) {
POST_SAVE.disconnect(id);
}
pub async fn send_admin_post_save(ctx: AdminSaveContext) {
POST_SAVE.send(ctx).await;
}
pub fn connect_admin_pre_delete<F, Fut>(receiver: F) -> ReceiverId
where
F: Fn(AdminDeleteContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ()> + Send + 'static,
{
PRE_DELETE.connect(receiver)
}
pub fn disconnect_admin_pre_delete(id: ReceiverId) {
PRE_DELETE.disconnect(id);
}
pub async fn send_admin_pre_delete(ctx: AdminDeleteContext) {
PRE_DELETE.send(ctx).await;
}
pub fn connect_admin_post_delete<F, Fut>(receiver: F) -> ReceiverId
where
F: Fn(AdminDeleteContext) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ()> + Send + 'static,
{
POST_DELETE.connect(receiver)
}
pub fn disconnect_admin_post_delete(id: ReceiverId) {
POST_DELETE.disconnect(id);
}
pub async fn send_admin_post_delete(ctx: AdminDeleteContext) {
POST_DELETE.send(ctx).await;
}
pub fn receiver_count() -> usize {
PRE_SAVE.len() + POST_SAVE.len() + PRE_DELETE.len() + POST_DELETE.len()
}
pub fn clear_all() {
PRE_SAVE.clear();
POST_SAVE.clear();
PRE_DELETE.clear();
POST_DELETE.clear();
}
#[allow(dead_code)]
fn _ensure_any_imported(_: &dyn Any) {}