use crate::scope::Scope;
use crate::sidecar::EventForwarder;
use futures_core::future::BoxFuture;
use serde_json::Value;
use std::sync::Arc;
use tracing::{error, warn};
pub type AnyError = Box<dyn std::error::Error + Send + Sync + 'static>;
pub type EventName = String;
pub type EventHandler =
Arc<dyn Fn(&Scope, Value) -> BoxFuture<'static, Result<(), AnyError>> + Send + Sync>;
#[derive(Clone, Default)]
pub struct EventBus {
handlers: Arc<Vec<(EventName, EventHandler)>>,
forwarder: Option<Arc<EventForwarder>>,
}
impl EventBus {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn on(self, name: impl Into<EventName>, handler: EventHandler) -> Self {
let mut handlers: Vec<(EventName, EventHandler)> = match Arc::try_unwrap(self.handlers) {
Ok(handlers) => handlers,
Err(shared) => (*shared).clone(),
};
handlers.push((name.into(), handler));
Self {
handlers: Arc::new(handlers),
forwarder: None,
}
}
pub fn handlers(&self) -> &[(EventName, EventHandler)] {
&self.handlers
}
#[must_use]
pub(crate) fn forwarding_to(self, forwarder: EventForwarder) -> Self {
Self {
forwarder: Some(Arc::new(forwarder)),
..self
}
}
fn run_local(&self, scope: &Scope, name: &str, payload: &Value) -> usize {
let matched: Vec<&(EventName, EventHandler)> = self
.handlers
.iter()
.filter(|(handler_name, _)| handler_name == name)
.collect();
for (_, handler) in &matched {
let fut = handler(scope, payload.clone());
let event = name.to_string();
scope.defer.wait_until(Box::pin(async move {
if let Err(err) = fut.await {
error!(event = %event, error = %err, "event handler failed");
crate::logging::forward_internal_error(&format!(
"event handler failed for {event}: {err}"
));
}
}));
}
matched.len()
}
pub fn deliver_inbound(&self, scope: &Scope, name: &str, payload: &Value) -> usize {
self.run_local(scope, name, payload)
}
#[allow(clippy::needless_pass_by_value)]
pub fn emit_in(&self, scope: &Scope, name: &str, payload: Value) {
let handled = self.run_local(scope, name, &payload);
let forwarded = match self.forwarder.as_ref() {
Some(forwarder) if !forwarder.is_empty() => {
let forwarder = Arc::clone(forwarder);
let request_id = scope.request_id.clone();
let event = name.to_string();
scope.defer.wait_until(Box::pin(async move {
forwarder.forward(&request_id, &event, &payload).await;
}));
true
}
_ => false,
};
if handled == 0 && !forwarded {
let detail = format!("emitted event `{name}` has no registered handler");
warn!(event = %name, "{detail}");
crate::logging::forward_internal_error(&detail);
}
}
}