nasaga_runtime_core/
transaction.rs1use std::future::Future;
4
5use nasaga_backend::{SagaBackend, SagaTransactionRunError, SagaTransactionRunner};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum SagaTransactionError {
13 RollbackOnly,
15 CommitRejected,
17 CommitUncertain,
19 RollbackFailed,
21 Infrastructure,
23}
24
25impl SagaTransactionError {
26 pub fn requires_unbounded_redelivery(self) -> bool {
32 matches!(
33 self,
34 Self::CommitRejected | Self::CommitUncertain | Self::RollbackFailed
35 )
36 }
37}
38
39impl std::fmt::Display for SagaTransactionError {
40 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 formatter.write_str(match self {
48 Self::RollbackOnly => "Saga transaction rollback-only",
49 Self::CommitRejected => "Saga transaction commit rejected",
50 Self::CommitUncertain => "Saga transaction commit uncertain",
51 Self::RollbackFailed => "Saga transaction rollback failed",
52 Self::Infrastructure => "Saga transaction infrastructure failed",
53 })
54 }
55}
56
57impl std::error::Error for SagaTransactionError {}
58
59pub(crate) async fn run_for<B, T, F>(backend: &B, body: F) -> anyhow::Result<T>
67where
68 B: SagaBackend,
69 F: Future<Output = anyhow::Result<T>> + Send,
70 T: Send,
71{
72 backend
73 .transaction_runner()
74 .run(Box::pin(body))
75 .await
76 .map_err(map_error)
77}
78
79pub(crate) async fn run_authorized_for<B, T, F>(
89 backend: &B,
90 authorize: &(dyn Fn() -> anyhow::Result<()> + Send + Sync),
91 body: F,
92) -> anyhow::Result<T>
93where
94 B: SagaBackend,
95 F: Future<Output = anyhow::Result<T>> + Send,
96 T: Send,
97{
98 run_for(backend, async {
99 let outcome = {
100 let mut body = Box::pin(body);
101 std::future::poll_fn(|context| {
102 if let Err(error) = authorize() {
104 return std::task::Poll::Ready(Err(error));
105 }
106 body.as_mut().poll(context)
107 })
108 .await
109 }?;
110 authorize()?;
112 Ok(outcome)
113 })
114 .await
115}
116
117fn map_error(error: SagaTransactionRunError<anyhow::Error>) -> anyhow::Error {
125 match error {
126 SagaTransactionRunError::Rollback(error) => error,
127 SagaTransactionRunError::RollbackOnly => SagaTransactionError::RollbackOnly.into(),
128 SagaTransactionRunError::CommitRejected => SagaTransactionError::CommitRejected.into(),
129 SagaTransactionRunError::CommitUncertain => SagaTransactionError::CommitUncertain.into(),
130 SagaTransactionRunError::RollbackFailed => SagaTransactionError::RollbackFailed.into(),
131 SagaTransactionRunError::Infrastructure => SagaTransactionError::Infrastructure.into(),
132 }
133}