mirror of
https://github.com/pfzetto/eventi.git
synced 2024-11-21 14:22:49 +01:00
first implementation
This commit is contained in:
commit
de4c2e6bdc
3 changed files with 247 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
/Cargo.lock
|
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "eventi"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tokio = { version = "1.35.0", features = ["full"] }
|
||||||
|
async-trait = "0.1.74"
|
235
src/lib.rs
Normal file
235
src/lib.rs
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
use std::{
|
||||||
|
any::{Any, TypeId},
|
||||||
|
collections::HashMap,
|
||||||
|
future::Future,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use tokio::{sync::RwLock, task::JoinHandle};
|
||||||
|
|
||||||
|
type AnonEventArc = Arc<dyn Any + Send + Sync>;
|
||||||
|
type AnonHandlerBox = Box<dyn AnonHandlerTrait>;
|
||||||
|
pub type HandlerResult = ();
|
||||||
|
|
||||||
|
pub trait Event: Any + Send + Sync {}
|
||||||
|
#[async_trait]
|
||||||
|
pub trait Handler<E: Event>: 'static + Send + Sync {
|
||||||
|
async fn run(&self, event: Arc<E>, dispatcher: &dyn Dispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait Dispatcher: Send + Sync {
|
||||||
|
async fn dispatch_anon(&self, event: AnonEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl dyn Dispatcher {
|
||||||
|
pub async fn dispatch<E: Event>(&self, event: Arc<E>) {
|
||||||
|
self.dispatch_anon(AnonEvent::from(event)).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
trait AnonHandlerTrait: Send + Sync {
|
||||||
|
async fn run(&self, event: AnonEvent, dispatcher: &dyn Dispatcher);
|
||||||
|
fn clone(&self) -> AnonHandlerBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AnonEvent {
|
||||||
|
type_id: TypeId,
|
||||||
|
value: AnonEventArc,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SemiAnonHandler<E: Event>(Arc<dyn Handler<E>>);
|
||||||
|
|
||||||
|
pub struct AnonHandler {
|
||||||
|
event_id: TypeId,
|
||||||
|
value: AnonHandlerBox,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct Bus {
|
||||||
|
registered: Arc<RwLock<HashMap<TypeId, Vec<AnonHandler>>>>,
|
||||||
|
running: Arc<RwLock<Vec<JoinHandle<HandlerResult>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnonEvent {
|
||||||
|
pub fn deanon<E: Event>(self) -> Option<Arc<E>> {
|
||||||
|
self.value.downcast::<E>().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Event> SemiAnonHandler<E> {
|
||||||
|
pub fn new<H: Handler<E>>(handler: Arc<H>) -> Self {
|
||||||
|
Self(handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnonHandler {
|
||||||
|
pub fn new<H: Handler<E>, E: Event>(handler: Arc<H>) -> Self {
|
||||||
|
Self {
|
||||||
|
event_id: TypeId::of::<E>(),
|
||||||
|
value: Box::new(SemiAnonHandler::new(handler)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub async fn run(&self, event: AnonEvent, dispatcher: &dyn Dispatcher) {
|
||||||
|
let handler = self.value.clone();
|
||||||
|
handler.run(event, dispatcher).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bus {
|
||||||
|
pub async fn subscribe<H: Handler<E>, E: Event>(&self, handler: H) {
|
||||||
|
let handler = AnonHandler::new(Arc::new(handler));
|
||||||
|
self.subscribe_anon(handler).await;
|
||||||
|
}
|
||||||
|
pub async fn subscribe_shared<H: Handler<E>, E: Event>(&self, handler: Arc<H>) {
|
||||||
|
let handler = AnonHandler::new(handler);
|
||||||
|
self.subscribe_anon(handler).await;
|
||||||
|
}
|
||||||
|
pub async fn subscribe_anon(&self, handler: AnonHandler) {
|
||||||
|
let registered = &mut self.registered.write().await;
|
||||||
|
if let Some(handlers) = registered.get_mut(&handler.event_id) {
|
||||||
|
handlers.push(handler);
|
||||||
|
} else {
|
||||||
|
registered.insert(handler.event_id, vec![handler]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn shutdown(&self) {
|
||||||
|
let mut running = self.running.write().await;
|
||||||
|
let mut new_running = vec![];
|
||||||
|
std::mem::swap(running.as_mut(), &mut new_running);
|
||||||
|
drop(running);
|
||||||
|
for running in new_running {
|
||||||
|
running.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Dispatcher for Bus {
|
||||||
|
async fn dispatch_anon(&self, event: AnonEvent) {
|
||||||
|
let mut new_running = self
|
||||||
|
.registered
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.get(&event.type_id)
|
||||||
|
.iter()
|
||||||
|
.flat_map(|x| x.iter())
|
||||||
|
.map(|handler| {
|
||||||
|
let handler = handler.clone();
|
||||||
|
let dispatcher = self.clone();
|
||||||
|
let event = event.clone();
|
||||||
|
tokio::spawn(async move { handler.run(event, &dispatcher).await })
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
self.running.write().await.append(&mut new_running);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Event> From<Arc<E>> for AnonEvent {
|
||||||
|
fn from(value: Arc<E>) -> Self {
|
||||||
|
Self {
|
||||||
|
type_id: TypeId::of::<E>(),
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Event> From<E> for AnonEvent {
|
||||||
|
fn from(value: E) -> Self {
|
||||||
|
Arc::new(value).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<E: Event> AnonHandlerTrait for SemiAnonHandler<E> {
|
||||||
|
async fn run(&self, event: AnonEvent, dispatcher: &dyn Dispatcher) {
|
||||||
|
if let Some(event) = event.deanon::<E>() {
|
||||||
|
self.0.run(event, dispatcher).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone(&self) -> AnonHandlerBox {
|
||||||
|
Box::new(Self(self.0.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for AnonHandler {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
event_id: self.event_id,
|
||||||
|
value: self.value.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: doesn't work
|
||||||
|
#[async_trait]
|
||||||
|
impl<F, E, Fut> Handler<E> for F
|
||||||
|
where
|
||||||
|
F: Fn(Arc<E>, &dyn Dispatcher) -> Fut + Send + Sync + 'static,
|
||||||
|
Fut: Future<Output = HandlerResult> + Send + Sync,
|
||||||
|
E: Event,
|
||||||
|
{
|
||||||
|
async fn run(&self, event: Arc<E>, dispatcher: &dyn Dispatcher) {
|
||||||
|
(self)(event, dispatcher).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::{
|
||||||
|
assert_eq,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicU8, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use crate::{AnonEvent, Bus, Dispatcher, Event, Handler};
|
||||||
|
|
||||||
|
struct Hello {
|
||||||
|
callback: AtomicU8,
|
||||||
|
}
|
||||||
|
impl Event for Hello {}
|
||||||
|
|
||||||
|
struct Hello2 {}
|
||||||
|
impl Event for Hello2 {}
|
||||||
|
|
||||||
|
struct HelloHandler;
|
||||||
|
#[async_trait]
|
||||||
|
impl Handler<Hello> for HelloHandler {
|
||||||
|
async fn run(&self, event: Arc<Hello>, dispatcher: &dyn Dispatcher) {
|
||||||
|
event.callback.fetch_add(1, Ordering::AcqRel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn basic() {
|
||||||
|
let bus = Bus::default();
|
||||||
|
|
||||||
|
bus.subscribe(HelloHandler).await;
|
||||||
|
|
||||||
|
let event1 = Arc::new(Hello {
|
||||||
|
callback: AtomicU8::new(0),
|
||||||
|
});
|
||||||
|
let event2 = Arc::new(Hello {
|
||||||
|
callback: AtomicU8::new(0),
|
||||||
|
});
|
||||||
|
|
||||||
|
bus.dispatch_anon(AnonEvent::from(event1.clone())).await;
|
||||||
|
bus.dispatch_anon(AnonEvent::from(event2.clone())).await;
|
||||||
|
bus.dispatch_anon(AnonEvent::from(event2.clone())).await;
|
||||||
|
|
||||||
|
bus.shutdown().await;
|
||||||
|
|
||||||
|
assert_eq!(event1.callback.load(Ordering::Acquire), 1);
|
||||||
|
assert_eq!(event2.callback.load(Ordering::Acquire), 2);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue