dipper 0.5.3

An out-of-the-box modular dependency injection web application framework.
Documentation
use std::{ops::Deref, sync::Arc};

use sea_orm::{DatabaseConnection, DbErr};

use crate::Depot;

/// Handle master-replica database connection pools depending on the backend
/// enabled by the feature flags.
#[derive(Clone, Debug, Default)]
pub struct DbService {
    master: MrConn,
    replica: MrConn,
}

/// Distinguish between master and replica database connections.
#[derive(Clone, Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum MrConn {
    Master(Arc<DatabaseConnection>),
    Replica(Arc<DatabaseConnection>),
}

impl Default for MrConn {
    fn default() -> Self {
        MrConn::Master(Default::default())
    }
}

impl Deref for MrConn {
    type Target = DatabaseConnection;

    fn deref(&self) -> &Self::Target {
        self.db()
    }
}

impl MrConn {
    /// Get a reference to the database connection object.
    pub fn db(&self) -> &DatabaseConnection {
        match self {
            MrConn::Master(arc) => arc,
            MrConn::Replica(arc) => arc,
        }
    }
    /// Get a reference to the database connection object,
    /// and also tell whether it is the master database.
    pub fn expand(&self) -> (&DatabaseConnection, bool) {
        match self {
            MrConn::Master(arc) => (arc, true),
            MrConn::Replica(arc) => (arc, false),
        }
    }
}

impl DbService {
    /// Create a database connection pool from the connection string.
    pub async fn new(master_url: &str, replica_url: &str) -> Result<Self, DbErr> {
        let master = Arc::new(sea_orm::Database::connect(master_url).await?);
        let replica = if master_url == replica_url {
            master.clone()
        } else {
            Arc::new(sea_orm::Database::connect(replica_url).await?)
        };
        Ok(DbService {
            master: MrConn::Master(master),
            replica: MrConn::Replica(replica),
        })
    }

    /// Encapsulate from two existing connection pools.
    pub const fn from(master: Arc<DatabaseConnection>, replica: Arc<DatabaseConnection>) -> Self {
        Self {
            master: MrConn::Master(master),
            replica: MrConn::Replica(replica),
        }
    }

    /// Get master database connection pool for writing.
    #[inline(always)]
    pub fn write_db(&self, depot: &mut Depot) -> &DatabaseConnection {
        self.master_db(Some(depot))
    }

    /// Get sensible database connection pool for reading.
    /// If the upstream uses the master database connection,
    /// return the master one; otherwise, return the replica one.
    #[inline(always)]
    pub fn read_db<'a, 'b: 'a>(&'b self, depot: &'a mut Depot) -> &'a DatabaseConnection {
        self.upstream_or_replica_db(depot)
    }

    /// Get master database connection pool.
    /// When depot is not None, record this selection to provide guidance for
    /// downstream calls.
    pub fn master_db(&self, depot: Option<&mut Depot>) -> &DatabaseConnection {
        if let Some(depot) = depot {
            depot.inject(self.master.clone());
        }
        &self.master
    }

    /// Get replica database connection pool.
    /// When depot is not None, record this selection to provide guidance for
    /// downstream calls.
    pub fn replica_db(&self, depot: Option<&mut Depot>) -> &DatabaseConnection {
        if let Some(depot) = depot {
            depot.inject(self.replica.clone());
        }
        &self.replica
    }

    /// Return the database connection object used by the upstream operation.
    /// If upstream is None, use the master database connection by default.
    pub fn upstream_or_master_db<'a, 'b: 'a>(&'b self, depot: &'a mut Depot) -> &'a MrConn {
        depot.obtain().unwrap_or(&self.master)
    }

    /// Return the database connection object used by the upstream operation.
    /// If upstream is None, use the replica database connection by default.
    pub fn upstream_or_replica_db<'a, 'b: 'a>(&'b self, depot: &'a mut Depot) -> &'a MrConn {
        depot.obtain().unwrap_or(&self.replica)
    }
}