use std::fmt;
use std::ops::{Deref, DerefMut};
use sqlx::pool::PoolConnection;
use sqlx::{Database, Error, Pool, SqlStr};
use sqlx_core::transaction::TransactionManager;
use crate::inbox::form::advisory::session::Closing;
pub(crate) struct PoolTx<DB: Database> {
conn: Option<PoolConnection<DB>>,
open: bool,
opened_with: Option<&'static str>,
claim: Option<&'static Closing>,
}
impl<DB: Database> PoolTx<DB> {
pub(crate) async fn begin(
pool: &Pool<DB>,
statement: Option<&'static str>,
) -> Result<Self, Error> {
Self::open(pool.acquire().await?, statement).await
}
pub(crate) async fn open(
conn: PoolConnection<DB>,
statement: Option<&'static str>,
) -> Result<Self, Error> {
let mut tx = Self {
conn: Some(conn),
open: true,
opened_with: statement,
claim: None,
};
DB::TransactionManager::begin(&mut *tx, statement.map(SqlStr::from_static)).await?;
Ok(tx)
}
pub(crate) const fn opened_with(&self) -> Option<&'static str> {
self.opened_with
}
pub(crate) async fn begin_claim(
pool: &Pool<DB>,
statement: Option<&'static str>,
closing: &'static Closing,
) -> Result<Self, Error> {
let conn = pool.acquire().await?;
let mut tx = Self {
conn: Some(conn),
open: true,
opened_with: statement,
claim: Some(closing),
};
DB::TransactionManager::begin(&mut *tx, statement.map(SqlStr::from_static)).await?;
Ok(tx)
}
pub(crate) fn claimed(&mut self) {
self.claim = None;
}
pub(crate) async fn commit(mut self) -> Result<(), Error> {
DB::TransactionManager::commit(&mut *self).await?;
self.open = false;
Ok(())
}
pub(crate) async fn rollback(mut self) -> Result<(), Error> {
DB::TransactionManager::rollback(&mut *self).await?;
self.open = false;
Ok(())
}
}
impl<DB: Database> Deref for PoolTx<DB> {
type Target = DB::Connection;
fn deref(&self) -> &Self::Target {
self.conn
.as_ref()
.expect("a transaction holds its connection until it drops")
}
}
impl<DB: Database> DerefMut for PoolTx<DB> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.conn
.as_mut()
.expect("a transaction holds its connection until it drops")
}
}
impl<DB: Database> Drop for PoolTx<DB> {
fn drop(&mut self) {
if !self.open {
return;
}
let Some(mut conn) = self.conn.take() else {
return;
};
match self.claim {
Some(closing) => closing.drain(conn, true),
None => conn.close_on_drop(),
}
}
}
impl<DB: Database> fmt::Debug for PoolTx<DB> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PoolTx")
.field("open", &self.open)
.finish_non_exhaustive()
}
}