use std::sync::Arc;
use futures::StreamExt;
use crate::{
command::{CommandContext, CommandError, CommandHandler, CommandIdStatic},
event::{MEvent, MEventType},
item::Eventable,
};
pub type EventStream = futures::stream::BoxStream<'static, MEvent>;
pub type CommandStream = futures::stream::BoxStream<'static, Box<dyn SagaCommand>>;
pub trait SagaCommand: Send + Sync + 'static {
fn execute_boxed(self: Box<Self>, ctx: CommandContext) -> Result<(), CommandError>;
fn command_name(&self) -> &'static str;
}
impl<C> SagaCommand for C
where
C: CommandHandler + CommandIdStatic + Send + Sync + 'static,
{
fn execute_boxed(self: Box<Self>, ctx: CommandContext) -> Result<(), CommandError> {
let _ = (*self).execute(ctx)?;
Ok(())
}
fn command_name(&self) -> &'static str {
C::COMMAND_ID
}
}
pub trait Saga: Send + Sync + 'static {
type State: Default + Send + Sync + 'static;
type Command: SagaCommand;
fn name() -> &'static str
where
Self: Sized;
fn build(
events: EventStream,
ctx: Arc<super::context::SagaContext>,
) -> futures::stream::BoxStream<'static, Self::Command>
where
Self: Sized;
}
pub trait SagaHandler: Send + Sync + 'static {
type EventItem: Eventable;
type Command: SagaCommand;
const EVENT_TYPE: MEventType;
fn handle(
item: Self::EventItem,
event: MEvent,
ctx: Arc<super::context::SagaContext>,
) -> Option<Self::Command>;
}
pub trait AnySaga: Send + Sync + 'static {
fn name(&self) -> &'static str;
fn build_boxed(
&self,
events: EventStream,
ctx: Arc<super::context::SagaContext>,
) -> CommandStream;
fn create_state(&self) -> Box<dyn std::any::Any + Send + Sync>;
}
impl<S: Saga> AnySaga for S {
fn name(&self) -> &'static str {
S::name()
}
fn build_boxed(
&self,
events: EventStream,
ctx: Arc<super::context::SagaContext>,
) -> CommandStream {
Box::pin(S::build(events, ctx).map(|cmd| Box::new(cmd) as Box<dyn SagaCommand>))
}
fn create_state(&self) -> Box<dyn std::any::Any + Send + Sync> {
Box::new(S::State::default())
}
}
pub struct SagaRegistration {
pub saga_id: &'static str,
pub create: fn() -> Arc<dyn AnySaga>,
pub event_entity_type: &'static str,
pub event_change_type: MEventType,
}
inventory::collect!(SagaRegistration);