1use std::sync::Arc;
6
7use futures::StreamExt;
8
9use crate::{
10 command::{CommandContext, CommandError, CommandHandler, CommandIdStatic},
11 event::{MEvent, MEventType},
12 item::Eventable,
13};
14
15pub type EventStream = futures::stream::BoxStream<'static, MEvent>;
17
18pub type CommandStream = futures::stream::BoxStream<'static, Box<dyn SagaCommand>>;
20
21pub trait SagaCommand: Send + Sync + 'static {
23 fn execute_boxed(self: Box<Self>, ctx: CommandContext) -> Result<(), CommandError>;
24 fn command_name(&self) -> &'static str;
25}
26
27impl<C> SagaCommand for C
28where
29 C: CommandHandler + CommandIdStatic + Send + Sync + 'static,
30{
31 fn execute_boxed(self: Box<Self>, ctx: CommandContext) -> Result<(), CommandError> {
32 let _ = (*self).execute(ctx)?;
33 Ok(())
34 }
35
36 fn command_name(&self) -> &'static str {
37 C::COMMAND_ID
38 }
39}
40
41pub trait Saga: Send + Sync + 'static {
43 type State: Default + Send + Sync + 'static;
45
46 type Command: SagaCommand;
48
49 fn name() -> &'static str
51 where
52 Self: Sized;
53
54 fn build(
56 events: EventStream,
57 ctx: Arc<super::context::SagaContext>,
58 ) -> futures::stream::BoxStream<'static, Self::Command>
59 where
60 Self: Sized;
61}
62
63pub trait SagaHandler: Send + Sync + 'static {
68 type EventItem: Eventable;
70 type Command: SagaCommand;
72 const EVENT_TYPE: MEventType;
74 fn handle(
76 item: Self::EventItem,
77 event: MEvent,
78 ctx: Arc<super::context::SagaContext>,
79 ) -> Option<Self::Command>;
80}
81
82pub trait AnySaga: Send + Sync + 'static {
84 fn name(&self) -> &'static str;
86
87 fn build_boxed(
89 &self,
90 events: EventStream,
91 ctx: Arc<super::context::SagaContext>,
92 ) -> CommandStream;
93
94 fn create_state(&self) -> Box<dyn std::any::Any + Send + Sync>;
96}
97
98impl<S: Saga> AnySaga for S {
99 fn name(&self) -> &'static str {
100 S::name()
101 }
102
103 fn build_boxed(
104 &self,
105 events: EventStream,
106 ctx: Arc<super::context::SagaContext>,
107 ) -> CommandStream {
108 Box::pin(S::build(events, ctx).map(|cmd| Box::new(cmd) as Box<dyn SagaCommand>))
109 }
110
111 fn create_state(&self) -> Box<dyn std::any::Any + Send + Sync> {
112 Box::new(S::State::default())
113 }
114}
115
116pub struct SagaRegistration {
118 pub saga_id: &'static str,
120 pub create: fn() -> Arc<dyn AnySaga>,
122 pub event_entity_type: &'static str,
124 pub event_change_type: MEventType,
126}
127
128inventory::collect!(SagaRegistration);