fmodel_rust/
saga_manager.rs

1use std::future::Future;
2use std::marker::PhantomData;
3
4use crate::saga::ActionComputation;
5
6/// Publishes the action/command to some external system.
7///
8/// Generic parameter:
9///
10/// - `A`. - action
11/// - `Error` - error
12#[cfg(not(feature = "not-send-futures"))]
13pub trait ActionPublisher<A, Error> {
14    /// Publishes the action/command to some external system, returning either the actions that are successfully published or error.
15    /// Desugared `async fn publish(&self, action: &[A]) -> Result<Vec<A>, Error>;` to a normal `fn` that returns `impl Future`, and adds bound `Send`.
16    /// You can freely move between the `async fn` and `-> impl Future` spelling in your traits and impls. This is true even when one form has a Send bound.
17    fn publish(&self, action: &[A]) -> impl Future<Output = Result<Vec<A>, Error>> + Send;
18}
19
20/// Publishes the action/command to some external system.
21///
22/// Generic parameter:
23///
24/// - `A`. - action
25/// - `Error` - error
26#[cfg(feature = "not-send-futures")]
27pub trait ActionPublisher<A, Error> {
28    /// Publishes the action/command to some external system, returning either the actions that are successfully published or error.
29    /// Desugared `async fn publish(&self, action: &[A]) -> Result<Vec<A>, Error>;` to a normal `fn` that returns `impl Future`.
30    /// You can freely move between the `async fn` and `-> impl Future` spelling in your traits and impls.
31    fn publish(&self, action: &[A]) -> impl Future<Output = Result<Vec<A>, Error>>;
32}
33
34/// Saga Manager.
35///
36/// It is using a `Saga` to react to the action result and to publish the new actions.
37/// It is using an [ActionPublisher] to publish the new actions.
38///
39/// Generic parameters:
40/// - `A` - Action / Command
41/// - `AR` - Action Result / Event
42/// - `Publisher` - Action Publisher
43/// - `Error` - Error
44pub struct SagaManager<A, AR, Publisher, Saga, Error>
45where
46    Publisher: ActionPublisher<A, Error>,
47    Saga: ActionComputation<AR, A>,
48{
49    action_publisher: Publisher,
50    saga: Saga,
51    _marker: PhantomData<(A, AR, Error)>,
52}
53
54impl<A, AR, Publisher, Saga, Error> ActionComputation<AR, A>
55    for SagaManager<A, AR, Publisher, Saga, Error>
56where
57    Publisher: ActionPublisher<A, Error>,
58    Saga: ActionComputation<AR, A>,
59{
60    /// Computes new actions based on the action result.
61    fn compute_new_actions(&self, action_result: &AR) -> Vec<A> {
62        self.saga.compute_new_actions(action_result)
63    }
64}
65
66#[cfg(not(feature = "not-send-futures"))]
67impl<A, AR, Publisher, Saga, Error> ActionPublisher<A, Error>
68    for SagaManager<A, AR, Publisher, Saga, Error>
69where
70    Publisher: ActionPublisher<A, Error> + Sync,
71    Saga: ActionComputation<AR, A> + Sync,
72    A: Sync,
73    AR: Sync,
74    Error: Sync,
75{
76    /// Publishes the action/command to some external system, returning either the actions that are successfully published or error.
77    async fn publish(&self, action: &[A]) -> Result<Vec<A>, Error> {
78        self.action_publisher.publish(action).await
79    }
80}
81
82#[cfg(feature = "not-send-futures")]
83impl<A, AR, Publisher, Saga, Error> ActionPublisher<A, Error>
84    for SagaManager<A, AR, Publisher, Saga, Error>
85where
86    Publisher: ActionPublisher<A, Error>,
87    Saga: ActionComputation<AR, A>,
88{
89    /// Publishes the action/command to some external system, returning either the actions that are successfully published or error.
90    async fn publish(&self, action: &[A]) -> Result<Vec<A>, Error> {
91        self.action_publisher.publish(action).await
92    }
93}
94
95#[cfg(not(feature = "not-send-futures"))]
96impl<A, AR, Publisher, Saga, Error> SagaManager<A, AR, Publisher, Saga, Error>
97where
98    Publisher: ActionPublisher<A, Error> + Sync,
99    Saga: ActionComputation<AR, A> + Sync,
100    A: Sync,
101    AR: Sync,
102    Error: Sync,
103{
104    /// Creates a new instance of [SagaManager].
105    pub fn new(action_publisher: Publisher, saga: Saga) -> Self {
106        SagaManager {
107            action_publisher,
108            saga,
109            _marker: PhantomData,
110        }
111    }
112    /// Handles the `action result` by computing new `actions` based on `action result`, and publishing new `actions` to the external system.
113    /// In most cases:
114    ///  - the `action result` is an `event` that you react,
115    ///  - the `actions` are `commands` that you publish downstream.
116    pub async fn handle(&self, action_result: &AR) -> Result<Vec<A>, Error> {
117        let new_actions = self.compute_new_actions(action_result);
118        let published_actions = self.publish(&new_actions).await?;
119        Ok(published_actions)
120    }
121}
122
123#[cfg(feature = "not-send-futures")]
124impl<A, AR, Publisher, Saga, Error> SagaManager<A, AR, Publisher, Saga, Error>
125where
126    Publisher: ActionPublisher<A, Error>,
127    Saga: ActionComputation<AR, A>,
128{
129    /// Creates a new instance of [SagaManager].
130    pub fn new(action_publisher: Publisher, saga: Saga) -> Self {
131        SagaManager {
132            action_publisher,
133            saga,
134            _marker: PhantomData,
135        }
136    }
137    /// Handles the `action result` by computing new `actions` based on `action result`, and publishing new `actions` to the external system.
138    /// In most cases:
139    ///  - the `action result` is an `event` that you react,
140    ///  - the `actions` are `commands` that you publish downstream.
141    pub async fn handle(&self, action_result: &AR) -> Result<Vec<A>, Error> {
142        let new_actions = self.compute_new_actions(action_result);
143        let published_actions = self.publish(&new_actions).await?;
144        Ok(published_actions)
145    }
146}