use serde_json::value::RawValue;
use crate::types::{
ApplicationAuthorized, ApplicationDeauthorized, Entitlement, Event, EventBody, LobbyMessage,
LobbyMessageDelete, Snowflake,
};
use super::{HandlerError, NoHandler, WithFallback};
pub struct EventCtx<'a, S> {
state: &'a S,
application_id: Snowflake,
timestamp: &'a str,
}
impl<S> Clone for EventCtx<'_, S> {
fn clone(&self) -> Self {
*self
}
}
impl<S> Copy for EventCtx<'_, S> {}
impl<'a, S> EventCtx<'a, S> {
pub fn new(state: &'a S, application_id: Snowflake, timestamp: &'a str) -> Self {
Self {
state,
application_id,
timestamp,
}
}
pub fn state(&self) -> &'a S {
self.state
}
pub fn application_id(&self) -> Snowflake {
self.application_id
}
pub fn timestamp(&self) -> &'a str {
self.timestamp
}
}
#[cfg(feature = "rest")]
impl<S: super::Platform> EventCtx<'_, S> {
pub fn bot(&self) -> Result<crate::rest::BotClient<S::Transport>, HandlerError> {
let token = self
.state
.bot_token()
.ok_or_else(|| HandlerError::new("the bot token (DISCORD_TOKEN) is not configured"))?;
Ok(crate::rest::BotClient::new(
self.state.transport(),
&token,
self.application_id,
))
}
}
pub trait EventHandler<S: Sync>: Sync {
fn handle(
&self,
ctx: EventCtx<'_, S>,
event: Event,
) -> impl Future<Output = Result<(), HandlerError>> + Send {
async move {
match event {
Event::ApplicationAuthorized(e) => self.application_authorized(ctx, e).await,
Event::ApplicationDeauthorized(e) => self.application_deauthorized(ctx, e).await,
Event::EntitlementCreate(e) => self.entitlement_create(ctx, e).await,
Event::EntitlementUpdate(e) => self.entitlement_update(ctx, e).await,
Event::EntitlementDelete(e) => self.entitlement_delete(ctx, e).await,
Event::LobbyMessageCreate(e) => self.lobby_message_create(ctx, e).await,
Event::LobbyMessageUpdate(e) => self.lobby_message_update(ctx, e).await,
Event::LobbyMessageDelete(e) => self.lobby_message_delete(ctx, e).await,
Event::Unknown { kind, data } => self.unknown(ctx, &kind, data.as_deref()).await,
}
}
}
fn application_authorized(
&self,
_ctx: EventCtx<'_, S>,
_event: ApplicationAuthorized,
) -> impl Future<Output = Result<(), HandlerError>> + Send {
async { Ok(()) }
}
fn application_deauthorized(
&self,
_ctx: EventCtx<'_, S>,
_event: ApplicationDeauthorized,
) -> impl Future<Output = Result<(), HandlerError>> + Send {
async { Ok(()) }
}
fn entitlement_create(
&self,
_ctx: EventCtx<'_, S>,
_entitlement: Entitlement,
) -> impl Future<Output = Result<(), HandlerError>> + Send {
async { Ok(()) }
}
fn entitlement_update(
&self,
_ctx: EventCtx<'_, S>,
_entitlement: Entitlement,
) -> impl Future<Output = Result<(), HandlerError>> + Send {
async { Ok(()) }
}
fn entitlement_delete(
&self,
_ctx: EventCtx<'_, S>,
_entitlement: Entitlement,
) -> impl Future<Output = Result<(), HandlerError>> + Send {
async { Ok(()) }
}
fn lobby_message_create(
&self,
_ctx: EventCtx<'_, S>,
_message: LobbyMessage,
) -> impl Future<Output = Result<(), HandlerError>> + Send {
async { Ok(()) }
}
fn lobby_message_update(
&self,
_ctx: EventCtx<'_, S>,
_message: LobbyMessage,
) -> impl Future<Output = Result<(), HandlerError>> + Send {
async { Ok(()) }
}
fn lobby_message_delete(
&self,
_ctx: EventCtx<'_, S>,
_message: LobbyMessageDelete,
) -> impl Future<Output = Result<(), HandlerError>> + Send {
async { Ok(()) }
}
fn unknown(
&self,
_ctx: EventCtx<'_, S>,
_kind: &str,
_data: Option<&RawValue>,
) -> impl Future<Output = Result<(), HandlerError>> + Send {
async { Ok(()) }
}
}
impl<S: Sync> EventHandler<S> for NoHandler {}
impl<S: Sync, C: EventHandler<S>, F: Sync> EventHandler<S> for WithFallback<C, F> {
async fn handle(&self, ctx: EventCtx<'_, S>, event: Event) -> Result<(), HandlerError> {
self.commands().handle(ctx, event).await
}
}
pub async fn dispatch_event<S: Sync, H: EventHandler<S>>(
handler: &H,
state: &S,
application_id: Snowflake,
body: &EventBody,
) -> Result<(), HandlerError> {
let ctx = EventCtx::new(state, application_id, &body.timestamp);
handler.handle(ctx, body.parse()).await
}
#[cfg(feature = "rest")]
pub fn spawn_event<S: super::Platform, H: EventHandler<S> + Send + 'static>(
handler: H,
state: &S,
application_id: Snowflake,
body: EventBody,
) {
use crate::trace::Instrument;
let owned = state.clone();
let span = info_span!("event", event_type = %body.kind);
let work = async move {
debug!("event received");
let result = dispatch_event(&handler, &owned, application_id, &body).await;
#[cfg(feature = "tracing")]
match &result {
Ok(()) => debug!("event handled"),
Err(error) => error.trace("event failed"),
}
result
}
.instrument(span.clone());
span.in_scope(|| state.spawn(Box::pin(work)));
}