floz-orm 0.1.7

A lightweight, typesafe Rust ORM — unifying DAO and DSL from a single schema
Documentation
//! `Tx` — polymorphic database transaction.
//!
//! Wraps a pooled connection with an active transaction.
//! If `commit()` is not called before the `Tx` is dropped,
//! the transaction is automatically rolled back when the connection
//! returns to the pool.

use tokio::sync::Mutex;

use crate::error::FlozError;
use super::placeholder::replace_pg_placeholders;

/// Inner transaction enum — one variant per enabled backend.
pub(crate) enum TxInner {
    #[cfg(feature = "postgres")]
    Postgres(Mutex<sqlx::pool::PoolConnection<sqlx::Postgres>>),
    #[cfg(feature = "sqlite")]
    Sqlite(Mutex<sqlx::pool::PoolConnection<sqlx::Sqlite>>),
}

/// A database transaction.
pub struct Tx {
    pub(crate) inner: TxInner,
    pub(crate) committed: bool,
}

impl Tx {
    /// Commit the transaction.
    #[allow(unreachable_patterns)]
    pub async fn commit(mut self) -> Result<(), FlozError> {
        match &self.inner {
            #[cfg(feature = "postgres")]
            TxInner::Postgres(conn) => {
                let mut c = conn.lock().await;
                sqlx::query("COMMIT").execute(&mut **c).await?;
            }
            #[cfg(feature = "sqlite")]
            TxInner::Sqlite(conn) => {
                let mut c = conn.lock().await;
                sqlx::query("COMMIT").execute(&mut **c).await?;
            }
            _ => unreachable!("no database backend enabled"),
        }
        self.committed = true;
        Ok(())
    }

    /// Explicitly rollback the transaction.
    #[allow(unreachable_patterns)]
    pub async fn rollback(self) -> Result<(), FlozError> {
        match &self.inner {
            #[cfg(feature = "postgres")]
            TxInner::Postgres(conn) => {
                let mut c = conn.lock().await;
                sqlx::query("ROLLBACK").execute(&mut **c).await?;
            }
            #[cfg(feature = "sqlite")]
            TxInner::Sqlite(conn) => {
                let mut c = conn.lock().await;
                sqlx::query("ROLLBACK").execute(&mut **c).await?;
            }
            _ => unreachable!("no database backend enabled"),
        }
        Ok(())
    }

    /// Adjust SQL placeholder syntax for the current backend.
    #[allow(unreachable_patterns)]
    pub(crate) fn adjust_sql(&self, sql: &str) -> String {
        match &self.inner {
            #[cfg(feature = "postgres")]
            TxInner::Postgres(_) => sql.to_string(),
            #[cfg(feature = "sqlite")]
            TxInner::Sqlite(_) => replace_pg_placeholders(sql),
            _ => unreachable!("no database backend enabled"),
        }
    }
}

impl std::fmt::Debug for Tx {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Tx")
            .field("committed", &self.committed)
            .finish()
    }
}