runique 3.0.2

A Django-inspired web framework for Rust with ORM, templates, and comprehensive security middleware
Documentation
//! `RuniqueDb`: a database handle that's either a real connection or a
//! transaction that's already open. It's what [`ADb`](crate::utils::aliases::ADb)
//! wraps when the `test-utils` feature is on.
//!
//! The point: every function written against `&ADb` (the framework's whole
//! database-facing side: `search!`, forms, admin) works the same whether it
//! gets a real connection or a transaction the test harness opened for a single
//! test and rolls back at the end, Django `TestCase` style. No `#[serial]`, no
//! tests stepping on each other in a shared database.
#![cfg(feature = "test-utils")]

use sea_orm::{
    AccessMode, ConnectionTrait, DatabaseConnection, DatabaseTransaction, DbBackend, DbErr,
    ExecResult, IsolationLevel, QueryResult, Statement, TransactionError, TransactionOptions,
    TransactionTrait,
};
use std::future::Future;
use std::pin::Pin;

/// Either a real [`DatabaseConnection`] or an already-open
/// [`DatabaseTransaction`]. The test harness uses the latter to wrap a test,
/// so nothing that test writes ever actually gets committed.
#[derive(Debug)]
pub enum RuniqueDb {
    Conn(DatabaseConnection),
    Txn(DatabaseTransaction),
}

#[async_trait::async_trait]
impl ConnectionTrait for RuniqueDb {
    fn get_database_backend(&self) -> DbBackend {
        match self {
            Self::Conn(c) => c.get_database_backend(),
            Self::Txn(t) => t.get_database_backend(),
        }
    }

    async fn execute_raw(&self, stmt: Statement) -> Result<ExecResult, DbErr> {
        match self {
            Self::Conn(c) => c.execute_raw(stmt).await,
            Self::Txn(t) => t.execute_raw(stmt).await,
        }
    }

    async fn execute_unprepared(&self, sql: &str) -> Result<ExecResult, DbErr> {
        match self {
            Self::Conn(c) => c.execute_unprepared(sql).await,
            Self::Txn(t) => t.execute_unprepared(sql).await,
        }
    }

    async fn query_one_raw(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr> {
        match self {
            Self::Conn(c) => c.query_one_raw(stmt).await,
            Self::Txn(t) => t.query_one_raw(stmt).await,
        }
    }

    async fn query_all_raw(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr> {
        match self {
            Self::Conn(c) => c.query_all_raw(stmt).await,
            Self::Txn(t) => t.query_all_raw(stmt).await,
        }
    }
}

// `Transaction = DatabaseTransaction` in both branches on purpose, never `Self`.
// `Conn(c).begin()` opens a normal transaction and `Txn(t).begin()` opens a
// savepoint, but either way you get a real `DatabaseTransaction`, which already
// handles nesting on its own. So there's no recursive nesting to reimplement here.
#[async_trait::async_trait]
impl TransactionTrait for RuniqueDb {
    type Transaction = DatabaseTransaction;

    async fn begin(&self) -> Result<Self::Transaction, DbErr> {
        match self {
            Self::Conn(c) => c.begin().await,
            Self::Txn(t) => t.begin().await,
        }
    }

    async fn begin_with_config(
        &self,
        isolation_level: Option<IsolationLevel>,
        access_mode: Option<AccessMode>,
    ) -> Result<Self::Transaction, DbErr> {
        match self {
            Self::Conn(c) => c.begin_with_config(isolation_level, access_mode).await,
            Self::Txn(t) => t.begin_with_config(isolation_level, access_mode).await,
        }
    }

    async fn begin_with_options(
        &self,
        options: TransactionOptions,
    ) -> Result<Self::Transaction, DbErr> {
        match self {
            Self::Conn(c) => c.begin_with_options(options).await,
            Self::Txn(t) => t.begin_with_options(options).await,
        }
    }

    async fn transaction<F, T, E>(&self, callback: F) -> Result<T, TransactionError<E>>
    where
        F: for<'c> FnOnce(
                &'c Self::Transaction,
            ) -> Pin<Box<dyn Future<Output = Result<T, E>> + Send + 'c>>
            + Send,
        T: Send,
        E: std::fmt::Display + std::fmt::Debug + Send,
    {
        match self {
            Self::Conn(c) => c.transaction(callback).await,
            Self::Txn(t) => t.transaction(callback).await,
        }
    }

    async fn transaction_with_config<F, T, E>(
        &self,
        callback: F,
        isolation_level: Option<IsolationLevel>,
        access_mode: Option<AccessMode>,
    ) -> Result<T, TransactionError<E>>
    where
        F: for<'c> FnOnce(
                &'c Self::Transaction,
            ) -> Pin<Box<dyn Future<Output = Result<T, E>> + Send + 'c>>
            + Send,
        T: Send,
        E: std::fmt::Display + std::fmt::Debug + Send,
    {
        match self {
            Self::Conn(c) => {
                c.transaction_with_config(callback, isolation_level, access_mode)
                    .await
            }
            Self::Txn(t) => {
                t.transaction_with_config(callback, isolation_level, access_mode)
                    .await
            }
        }
    }
}