1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
use crate::ReactFunction;
/// [Saga] is a datatype that represents the central point of control, deciding what to execute next (`A`), based on the action result (`AR`).
/// It has two generic parameters `AR`/Action Result, `A`/Action , representing the type of the values that Saga may contain or use.
/// `'a` is used as a lifetime parameter, indicating that all references contained within the struct (e.g., references within the function closures) must have a lifetime that is at least as long as 'a.
///
/// It is common to consider Event as Action Result, and Command as Action, but it is not mandatory.
/// For example, Action Result can be a request response from a remote service.
///
/// ## Example
///
/// ```
/// use fmodel_rust::saga::Saga;
///
/// fn saga<'a>() -> Saga<'a, OrderEvent, ShipmentCommand> {
/// Saga {
/// react: Box::new(|event| match event {
/// OrderEvent::Created(created_event) => {
/// vec![ShipmentCommand::Create(CreateShipmentCommand {
/// shipment_id: created_event.order_id,
/// order_id: created_event.order_id,
/// customer_name: created_event.customer_name.to_owned(),
/// items: created_event.items.to_owned(),
/// })]
/// }
/// OrderEvent::Updated(_updated_event) => {
/// vec![]
/// }
/// OrderEvent::Cancelled(_cancelled_event) => {
/// vec![]
/// }
/// }),
/// }
/// }
///
/// #[derive(Debug, PartialEq)]
/// #[allow(dead_code)]
/// pub enum ShipmentCommand {
/// Create(CreateShipmentCommand),
/// }
///
/// #[derive(Debug, PartialEq)]
/// pub struct CreateShipmentCommand {
/// pub shipment_id: u32,
/// pub order_id: u32,
/// pub customer_name: String,
/// pub items: Vec<String>,
/// }
///
/// #[derive(Debug)]
/// pub enum OrderEvent {
/// Created(OrderCreatedEvent),
/// Updated(OrderUpdatedEvent),
/// Cancelled(OrderCancelledEvent),
/// }
///
/// #[derive(Debug)]
/// pub struct OrderCreatedEvent {
/// pub order_id: u32,
/// pub customer_name: String,
/// pub items: Vec<String>,
/// }
///
/// #[derive(Debug)]
/// pub struct OrderUpdatedEvent {
/// pub order_id: u32,
/// pub updated_items: Vec<String>,
/// }
///
/// #[derive(Debug)]
/// pub struct OrderCancelledEvent {
/// pub order_id: u32,
/// }
///
/// let saga: Saga<OrderEvent, ShipmentCommand> = saga();
/// let order_created_event = OrderEvent::Created(OrderCreatedEvent {
/// order_id: 1,
/// customer_name: "John Doe".to_string(),
/// items: vec!["Item 1".to_string(), "Item 2".to_string()],
/// });
///
/// let commands = (saga.react)(&order_created_event);
/// ```
pub struct Saga<'a, AR: 'a, A: 'a> {
/// The `react` function is driving the next action based on the action result.
pub react: ReactFunction<'a, AR, A>,
}
impl<'a, AR, A> Saga<'a, AR, A> {
/// Maps the Saga over the A/Action type parameter.
/// Creates a new instance of [Saga]`<AR, A2>`.
pub fn map_action<A2, F>(self, f: &'a F) -> Saga<'a, AR, A2>
where
F: Fn(&A) -> A2 + Send + Sync,
{
let new_react = Box::new(move |ar: &AR| {
let a = (self.react)(ar);
a.into_iter().map(|a: A| f(&a)).collect()
});
Saga { react: new_react }
}
/// Maps the Saga over the AR/ActionResult type parameter.
/// Creates a new instance of [Saga]`<AR2, A>`.
pub fn map_action_result<AR2, F>(self, f: &'a F) -> Saga<'a, AR2, A>
where
F: Fn(&AR2) -> AR + Send + Sync,
{
let new_react = Box::new(move |ar2: &AR2| {
let ar = f(ar2);
(self.react)(&ar)
});
Saga { react: new_react }
}
}