use crate::{
DatabaseBackend, DatabasePool, TestContext, TestDatabaseInstance, TransactionStarter,
transaction::DBTransactionManager,
};
use async_trait::async_trait;
use std::fmt::Debug;
#[async_trait]
pub trait DatabaseHandler<DB>: Send + Sync
where
DB: DatabaseBackend + Send + Sync + Debug + 'static,
{
async fn execute(&self, ctx: &mut TestContext<DB>) -> Result<(), DB::Error>;
}
#[async_trait]
impl<DB, F, Fut> DatabaseHandler<DB> for F
where
DB: DatabaseBackend + Send + Sync + Debug + 'static,
F: FnOnce(&mut TestContext<DB>) -> Fut + Send + Sync + Clone,
Fut: std::future::Future<Output = Result<(), DB::Error>> + Send + 'static,
{
async fn execute(&self, ctx: &mut TestContext<DB>) -> Result<(), DB::Error> {
self.clone()(ctx).await
}
}
#[derive(Debug)]
#[must_use]
pub struct TransactionHandler<DB, S, TFn>
where
DB: DatabaseBackend + Send + Sync + Debug + 'static,
S: Send + Sync + 'static,
TFn: Send + Sync + 'static,
{
db: TestDatabaseInstance<DB>,
setup_fn: S,
transaction_fn: TFn,
}
impl<DB, S, TFn> TransactionHandler<DB, S, TFn>
where
DB: DatabaseBackend + Send + Sync + Debug + 'static,
S: Send + Sync + 'static,
TFn: Send + Sync + 'static,
{
#[allow(dead_code)]
#[inline]
pub fn new(db: TestDatabaseInstance<DB>, setup_fn: S, transaction_fn: TFn) -> Self {
Self {
db,
setup_fn,
transaction_fn,
}
}
}
impl<DB, S, Fut, TFn, TxFut> TransactionHandler<DB, S, TFn>
where
DB: DatabaseBackend + Send + Sync + Debug + 'static,
S: FnOnce(&mut <DB::Pool as DatabasePool>::Connection) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<Output = Result<(), DB::Error>> + Send + 'static,
TxFut: std::future::Future<Output = Result<(), DB::Error>> + Send + 'static,
for<'tx> TFn: FnOnce(&'tx mut <TestContext<DB> as TransactionStarter<DB>>::Transaction) -> TxFut
+ Send
+ Sync
+ 'static,
TestContext<DB>: TransactionStarter<DB>
+ DBTransactionManager<
<TestContext<DB> as TransactionStarter<DB>>::Transaction,
<TestContext<DB> as TransactionStarter<DB>>::Connection,
Error = DB::Error,
Tx = <TestContext<DB> as TransactionStarter<DB>>::Transaction,
>,
{
#[allow(dead_code)]
pub async fn execute(self) -> Result<TestContext<DB>, DB::Error> {
self.db.setup(self.setup_fn).await?;
let mut ctx = TestContext::new(self.db);
let mut tx = <TestContext<DB> as DBTransactionManager<
<TestContext<DB> as TransactionStarter<DB>>::Transaction,
<TestContext<DB> as TransactionStarter<DB>>::Connection,
>>::begin_transaction(&mut ctx)
.await?;
match (self.transaction_fn)(&mut tx).await {
Ok(_) => {
<TestContext<DB> as DBTransactionManager<
<TestContext<DB> as TransactionStarter<DB>>::Transaction,
<TestContext<DB> as TransactionStarter<DB>>::Connection,
>>::commit_transaction(&mut tx)
.await?;
}
Err(e) => {
let _ = <TestContext<DB> as DBTransactionManager<
<TestContext<DB> as TransactionStarter<DB>>::Transaction,
<TestContext<DB> as TransactionStarter<DB>>::Connection,
>>::rollback_transaction(&mut tx)
.await;
return Err(e);
}
}
Ok(ctx)
}
}