use crate::saga_action_error::ActionError;
use crate::saga_action_generic::Action;
use crate::saga_action_generic::ActionData;
use crate::saga_action_generic::ActionResult;
use crate::saga_action_generic::SagaType;
use crate::saga_action_generic::UndoResult;
use crate::saga_exec::ActionContext;
use crate::ActionName;
use futures::future::BoxFuture;
use std::any::type_name;
use std::fmt;
use std::fmt::Debug;
use std::future::Future;
use std::sync::Arc;
pub type ActionFuncResult<T, E> = Result<T, E>;
pub trait ActionFn<'c, S: SagaType>: Send + Sync + 'static {
type Output;
type Future: Future<Output = Self::Output> + Send + 'c;
fn act(&'c self, ctx: ActionContext<S>) -> Self::Future;
}
impl<'c, F, S, FF> ActionFn<'c, S> for F
where
S: SagaType,
F: Fn(ActionContext<S>) -> FF + Send + Sync + 'static,
FF: std::future::Future + Send + 'c,
{
type Future = FF;
type Output = FF::Output;
fn act(&'c self, ctx: ActionContext<S>) -> Self::Future {
self(ctx)
}
}
pub struct ActionFunc<ActionFuncType, UndoFuncType> {
name: ActionName,
action_func: ActionFuncType,
undo_func: UndoFuncType,
}
impl<ActionFuncType, UndoFuncType> ActionFunc<ActionFuncType, UndoFuncType> {
pub fn new_action<Name, UserType, ActionFuncOutput>(
name: Name,
action_func: ActionFuncType,
undo_func: UndoFuncType,
) -> Arc<dyn Action<UserType>>
where
Name: AsRef<str>,
UserType: SagaType,
for<'c> ActionFuncType: ActionFn<
'c,
UserType,
Output = ActionFuncResult<ActionFuncOutput, ActionError>,
>,
ActionFuncOutput: ActionData,
for<'c> UndoFuncType: ActionFn<'c, UserType, Output = UndoResult>,
{
Arc::new(ActionFunc {
name: ActionName::new(name.as_ref()),
action_func,
undo_func,
})
}
}
pub fn new_action_noop_undo<Name, UserType, ActionFuncType, ActionFuncOutput>(
name: Name,
f: ActionFuncType,
) -> Arc<dyn Action<UserType>>
where
Name: AsRef<str>,
UserType: SagaType,
for<'c> ActionFuncType: ActionFn<
'c,
UserType,
Output = ActionFuncResult<ActionFuncOutput, ActionError>,
>,
ActionFuncOutput: ActionData,
{
ActionFunc::new_action(name, f, |_| async { Ok(()) })
}
impl<UserType, ActionFuncType, ActionFuncOutput, UndoFuncType> Action<UserType>
for ActionFunc<ActionFuncType, UndoFuncType>
where
UserType: SagaType,
for<'c> ActionFuncType: ActionFn<
'c,
UserType,
Output = ActionFuncResult<ActionFuncOutput, ActionError>,
>,
ActionFuncOutput: ActionData,
for<'c> UndoFuncType: ActionFn<'c, UserType, Output = UndoResult>,
{
fn do_it(
&self,
sgctx: ActionContext<UserType>,
) -> BoxFuture<'_, ActionResult> {
Box::pin(async move {
let fut = self.action_func.act(sgctx);
fut.await
.and_then(|func_output| {
serde_json::to_value(func_output)
.map_err(ActionError::new_serialize)
})
.map(Arc::new)
})
}
fn undo_it(
&self,
sgctx: ActionContext<UserType>,
) -> BoxFuture<'_, UndoResult> {
Box::pin(self.undo_func.act(sgctx))
}
fn name(&self) -> ActionName {
self.name.clone()
}
}
impl<ActionFuncType, UndoFuncType> Debug
for ActionFunc<ActionFuncType, UndoFuncType>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&type_name::<ActionFuncType>())
}
}