ferro_rs/database/
transaction.rs1use async_trait::async_trait;
25use sea_orm::{
26 AccessMode, DatabaseConnection, DatabaseTransaction, IsolationLevel, TransactionTrait,
27};
28use std::future::Future;
29
30use crate::database::DB;
31use crate::error::FrameworkError;
32
33pub async fn transaction<F, T, Fut>(f: F) -> Result<T, FrameworkError>
61where
62 F: FnOnce(&DatabaseTransaction) -> Fut,
63 Fut: Future<Output = Result<T, FrameworkError>>,
64{
65 let db = DB::connection()?;
66 let txn = db
67 .inner()
68 .begin()
69 .await
70 .map_err(|e| FrameworkError::database(format!("Failed to begin transaction: {e}")))?;
71
72 match f(&txn).await {
73 Ok(result) => {
74 txn.commit()
75 .await
76 .map_err(|e| FrameworkError::database(format!("Failed to commit: {e}")))?;
77 Ok(result)
78 }
79 Err(e) => {
80 Err(e)
82 }
83 }
84}
85
86pub async fn transaction_with<F, T, Fut>(
100 isolation_level: IsolationLevel,
101 f: F,
102) -> Result<T, FrameworkError>
103where
104 F: FnOnce(&DatabaseTransaction) -> Fut,
105 Fut: Future<Output = Result<T, FrameworkError>>,
106{
107 let db = DB::connection()?;
108 let txn = db
109 .inner()
110 .begin_with_config(Some(isolation_level), Some(AccessMode::ReadWrite))
111 .await
112 .map_err(|e| FrameworkError::database(format!("Failed to begin transaction: {e}")))?;
113
114 match f(&txn).await {
115 Ok(result) => {
116 txn.commit()
117 .await
118 .map_err(|e| FrameworkError::database(format!("Failed to commit: {e}")))?;
119 Ok(result)
120 }
121 Err(e) => Err(e),
122 }
123}
124
125#[async_trait]
127pub trait TransactionExt {
128 async fn transaction<F, T, Fut>(&self, f: F) -> Result<T, FrameworkError>
141 where
142 F: FnOnce(&DatabaseTransaction) -> Fut + Send,
143 Fut: Future<Output = Result<T, FrameworkError>> + Send,
144 T: Send;
145
146 async fn transaction_with<F, T, Fut>(
148 &self,
149 isolation_level: IsolationLevel,
150 f: F,
151 ) -> Result<T, FrameworkError>
152 where
153 F: FnOnce(&DatabaseTransaction) -> Fut + Send,
154 Fut: Future<Output = Result<T, FrameworkError>> + Send,
155 T: Send;
156}
157
158#[async_trait]
159impl TransactionExt for DatabaseConnection {
160 async fn transaction<F, T, Fut>(&self, f: F) -> Result<T, FrameworkError>
161 where
162 F: FnOnce(&DatabaseTransaction) -> Fut + Send,
163 Fut: Future<Output = Result<T, FrameworkError>> + Send,
164 T: Send,
165 {
166 let txn = self
167 .begin()
168 .await
169 .map_err(|e| FrameworkError::database(format!("Failed to begin transaction: {e}")))?;
170
171 match f(&txn).await {
172 Ok(result) => {
173 txn.commit()
174 .await
175 .map_err(|e| FrameworkError::database(format!("Failed to commit: {e}")))?;
176 Ok(result)
177 }
178 Err(e) => Err(e),
179 }
180 }
181
182 async fn transaction_with<F, T, Fut>(
183 &self,
184 isolation_level: IsolationLevel,
185 f: F,
186 ) -> Result<T, FrameworkError>
187 where
188 F: FnOnce(&DatabaseTransaction) -> Fut + Send,
189 Fut: Future<Output = Result<T, FrameworkError>> + Send,
190 T: Send,
191 {
192 let txn = self
193 .begin_with_config(Some(isolation_level), Some(AccessMode::ReadWrite))
194 .await
195 .map_err(|e| FrameworkError::database(format!("Failed to begin transaction: {e}")))?;
196
197 match f(&txn).await {
198 Ok(result) => {
199 txn.commit()
200 .await
201 .map_err(|e| FrameworkError::database(format!("Failed to commit: {e}")))?;
202 Ok(result)
203 }
204 Err(e) => Err(e),
205 }
206 }
207}
208
209#[macro_export]
223macro_rules! txn {
224 ($($body:tt)*) => {
225 $crate::database::transaction(|_txn| async move {
226 $($body)*
227 }).await
228 };
229}
230
231pub use txn;