use std::fmt;
use std::ops::{Deref, DerefMut};
use std::thread;
use sqlx::Database;
use super::book::{TxBook, TxSlot};
use crate::inbox::database::QueueDatabase;
use crate::inbox::form::advisory::{LockBook, LockSlot, Locked};
use crate::inbox::tx::PoolTx;
pub struct Tx<DB: QueueDatabase> {
lent: Option<Lent<DB>>,
}
impl<DB: QueueDatabase> Tx<DB> {
pub(crate) fn lent_by(lender: Lender<DB>) -> Option<Self> {
Some(Self {
lent: Some(lender.lend()?),
})
}
fn lent(&self) -> &Lent<DB> {
self.lent
.as_ref()
.expect("a `Tx` holds its transaction until it drops")
}
fn lent_mut(&mut self) -> &mut Lent<DB> {
self.lent
.as_mut()
.expect("a `Tx` holds its transaction until it drops")
}
}
impl<DB: QueueDatabase> Deref for Tx<DB> {
type Target = DB::Connection;
fn deref(&self) -> &DB::Connection {
self.lent().conn()
}
}
impl<DB: QueueDatabase> DerefMut for Tx<DB> {
fn deref_mut(&mut self) -> &mut DB::Connection {
self.lent_mut().conn_mut()
}
}
impl<DB: QueueDatabase> AsMut<DB::Connection> for Tx<DB> {
fn as_mut(&mut self) -> &mut DB::Connection {
self
}
}
impl<DB: QueueDatabase> Drop for Tx<DB> {
fn drop(&mut self) {
if let Some(lent) = self.lent.take() {
lent.give_back(thread::panicking());
}
}
}
impl<DB: QueueDatabase> fmt::Debug for Tx<DB> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Tx").finish_non_exhaustive()
}
}
pub(crate) enum Lent<DB: Database> {
Tx {
book: &'static TxBook<DB>,
slot: TxSlot,
tx: PoolTx<DB>,
},
Session {
book: &'static LockBook<DB>,
slot: LockSlot,
locked: Locked<'static, DB>,
},
}
impl<DB: Database> Lent<DB> {
fn conn(&self) -> &DB::Connection {
match self {
Self::Tx { tx, .. } => tx,
Self::Session { locked, .. } => locked.conn_ref(),
}
}
fn conn_mut(&mut self) -> &mut DB::Connection {
match self {
Self::Tx { tx, .. } => tx,
Self::Session { locked, .. } => locked.conn(),
}
}
pub(super) fn give_back(self, panicked: bool) {
match self {
Self::Tx { book, slot, tx } => book.give_back(slot, tx, panicked),
Self::Session { book, slot, locked } => book.give_back(slot, locked, panicked),
}
}
}
pub(crate) enum Lender<DB: Database> {
Tx(&'static TxBook<DB>, TxSlot),
Lock(&'static LockBook<DB>, LockSlot),
}
impl<DB: Database> Clone for Lender<DB> {
fn clone(&self) -> Self {
*self
}
}
impl<DB: Database> Copy for Lender<DB> {}
impl<DB: Database> fmt::Debug for Lender<DB> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Tx(_, slot) => f.debug_tuple("Tx").field(slot).finish(),
Self::Lock(_, slot) => f.debug_tuple("Lock").field(slot).finish(),
}
}
}
impl<DB: Database> Lender<DB> {
pub(crate) fn lend(self) -> Option<Lent<DB>> {
match self {
Self::Tx(book, slot) => book.lend(slot).map(|tx| Lent::Tx { book, slot, tx }),
Self::Lock(book, slot) => {
book.lend(slot)
.map(|locked| Lent::Session { book, slot, locked })
}
}
}
}