use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use super::{BackgroundErrorHandler, BackgroundSpawner, BoxDynError, NoemaResult};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum InvokeMode {
#[default]
Spawn,
Await,
}
pub struct DispatchContext {
pub spawner: Arc<dyn BackgroundSpawner>,
pub error_handler: Arc<dyn BackgroundErrorHandler>,
pub invoke_mode: InvokeMode,
}
impl DispatchContext {
pub fn new(
spawner: Arc<dyn BackgroundSpawner>,
error_handler: Arc<dyn BackgroundErrorHandler>,
) -> Self {
Self {
spawner,
error_handler,
invoke_mode: InvokeMode::Spawn,
}
}
pub fn with_invoke_mode(mut self, invoke_mode: InvokeMode) -> Self {
self.invoke_mode = invoke_mode;
self
}
}
pub type ReceiveFn = fn(
payload: &[u8],
ctx: &DispatchContext,
) -> Pin<Box<dyn Future<Output = NoemaResult<()>> + Send>>;
pub struct SubscriberEntry {
pub name: &'static str,
pub receive: ReceiveFn,
}
pub trait SubscriberRegistry: Send + Sync {
fn entries(&self) -> &'static [SubscriberEntry];
}
pub trait EventDispatcherContext: Send + Sync {
fn dispatch_context(&self) -> DispatchContext;
}
#[async_trait::async_trait]
pub trait EventDispatch: SubscriberRegistry + EventDispatcherContext + Send + Sync {
async fn dispatch(&self, name: &str, payload: &[u8]) -> NoemaResult<()> {
let entry = match self.entries().iter().find(|e| e.name == name) {
Some(e) => e,
None => {
let err: BoxDynError = format!("unknown event: {name}").into();
self.dispatch_context()
.error_handler
.handle(err, name, "dispatch".to_string())
.await;
return Err(format!("unknown event: {name}").into());
}
};
(entry.receive)(payload, &self.dispatch_context()).await
}
}
impl<T> EventDispatch for T where T: SubscriberRegistry + EventDispatcherContext + Send + Sync {}