use async_trait::async_trait;
use axum::extract::FromRef;
use crate::{GotchaConfig, GotchaContext};
#[async_trait]
pub trait Message<S, C>: Send + 'static
where
S: Clone + Send + Sync + 'static,
C: GotchaConfig,
{
type Output: Send + 'static;
async fn handle(self, messager: Messager<S, C>) -> Self::Output;
}
pub struct Messager<S, C>
where
S: Clone + Send + Sync + 'static,
C: GotchaConfig,
{
context: GotchaContext<S, C>,
}
impl<S, C> Clone for Messager<S, C>
where
S: Clone + Send + Sync + 'static,
C: GotchaConfig,
{
fn clone(&self) -> Self {
Self { context: self.context.clone() }
}
}
impl<S, C> Messager<S, C>
where
S: Clone + Send + Sync + 'static,
C: GotchaConfig,
{
pub fn new(context: GotchaContext<S, C>) -> Self {
Self { context }
}
pub fn context(&self) -> &GotchaContext<S, C> {
&self.context
}
pub fn state(&self) -> &S {
&self.context.state
}
pub async fn send<M: Message<S, C>>(&self, message: M) -> M::Output {
message.handle(self.clone()).await
}
pub fn spawn<M: Message<S, C, Output = ()>>(&self, message: M) {
let messager = self.clone();
tokio::spawn(async move { message.handle(messager).await });
}
}
impl<S, C> FromRef<GotchaContext<S, C>> for Messager<S, C>
where
S: Clone + Send + Sync + 'static,
C: GotchaConfig,
{
fn from_ref(context: &GotchaContext<S, C>) -> Self {
Messager::new(context.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{ConfigWrapper, EmptyConfig};
#[derive(Clone, Default)]
struct AppState {
greeting: String,
}
struct Greet {
name: String,
}
#[async_trait]
impl Message<AppState, EmptyConfig> for Greet {
type Output = String;
async fn handle(self, messager: Messager<AppState, EmptyConfig>) -> String {
format!("{}, {}!", messager.state().greeting, self.name)
}
}
#[test]
fn send_dispatches_and_reads_state() {
let context = GotchaContext {
config: ConfigWrapper {
server: Default::default(),
app: EmptyConfig::default(),
},
state: AppState { greeting: "Hello".to_string() },
};
let messager = Messager::new(context);
let output = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap()
.block_on(messager.send(Greet { name: "world".to_string() }));
assert_eq!(output, "Hello, world!");
}
}