use async_trait::async_trait;
use sea_orm::{
AccessMode, DatabaseConnection, DatabaseTransaction, IsolationLevel, TransactionTrait,
};
use std::future::Future;
use crate::database::DB;
use crate::error::FrameworkError;
pub async fn transaction<F, T, Fut>(f: F) -> Result<T, FrameworkError>
where
F: FnOnce(&DatabaseTransaction) -> Fut,
Fut: Future<Output = Result<T, FrameworkError>>,
{
let db = DB::connection()?;
let txn = db
.inner()
.begin()
.await
.map_err(|e| FrameworkError::database(format!("Failed to begin transaction: {e}")))?;
match f(&txn).await {
Ok(result) => {
txn.commit()
.await
.map_err(|e| FrameworkError::database(format!("Failed to commit: {e}")))?;
Ok(result)
}
Err(e) => {
Err(e)
}
}
}
pub async fn transaction_with<F, T, Fut>(
isolation_level: IsolationLevel,
f: F,
) -> Result<T, FrameworkError>
where
F: FnOnce(&DatabaseTransaction) -> Fut,
Fut: Future<Output = Result<T, FrameworkError>>,
{
let db = DB::connection()?;
let txn = db
.inner()
.begin_with_config(Some(isolation_level), Some(AccessMode::ReadWrite))
.await
.map_err(|e| FrameworkError::database(format!("Failed to begin transaction: {e}")))?;
match f(&txn).await {
Ok(result) => {
txn.commit()
.await
.map_err(|e| FrameworkError::database(format!("Failed to commit: {e}")))?;
Ok(result)
}
Err(e) => Err(e),
}
}
#[async_trait]
pub trait TransactionExt {
async fn transaction<F, T, Fut>(&self, f: F) -> Result<T, FrameworkError>
where
F: FnOnce(&DatabaseTransaction) -> Fut + Send,
Fut: Future<Output = Result<T, FrameworkError>> + Send,
T: Send;
async fn transaction_with<F, T, Fut>(
&self,
isolation_level: IsolationLevel,
f: F,
) -> Result<T, FrameworkError>
where
F: FnOnce(&DatabaseTransaction) -> Fut + Send,
Fut: Future<Output = Result<T, FrameworkError>> + Send,
T: Send;
}
#[async_trait]
impl TransactionExt for DatabaseConnection {
async fn transaction<F, T, Fut>(&self, f: F) -> Result<T, FrameworkError>
where
F: FnOnce(&DatabaseTransaction) -> Fut + Send,
Fut: Future<Output = Result<T, FrameworkError>> + Send,
T: Send,
{
let txn = self
.begin()
.await
.map_err(|e| FrameworkError::database(format!("Failed to begin transaction: {e}")))?;
match f(&txn).await {
Ok(result) => {
txn.commit()
.await
.map_err(|e| FrameworkError::database(format!("Failed to commit: {e}")))?;
Ok(result)
}
Err(e) => Err(e),
}
}
async fn transaction_with<F, T, Fut>(
&self,
isolation_level: IsolationLevel,
f: F,
) -> Result<T, FrameworkError>
where
F: FnOnce(&DatabaseTransaction) -> Fut + Send,
Fut: Future<Output = Result<T, FrameworkError>> + Send,
T: Send,
{
let txn = self
.begin_with_config(Some(isolation_level), Some(AccessMode::ReadWrite))
.await
.map_err(|e| FrameworkError::database(format!("Failed to begin transaction: {e}")))?;
match f(&txn).await {
Ok(result) => {
txn.commit()
.await
.map_err(|e| FrameworkError::database(format!("Failed to commit: {e}")))?;
Ok(result)
}
Err(e) => Err(e),
}
}
}
#[macro_export]
macro_rules! txn {
($($body:tt)*) => {
$crate::database::transaction(|_txn| async move {
$($body)*
}).await
};
}
pub use txn;