use rbatis::{rbdc::db::Connection as RbdcConnection, RBatis};
#[cfg(feature = "mysql")]
use rbdc_mysql::{driver::MysqlDriver, options::MySqlConnectOptions};
#[cfg(feature = "postgres")]
use rbdc_pg::{driver::PgDriver, options::PgConnectOptions};
use std::sync::OnceLock;
#[cfg(any(feature = "mysql", feature = "postgres"))]
use {rbatis::DefaultPool, std::str::FromStr};
pub type Connection = Box<dyn RbdcConnection>;
static DB_POOL: OnceLock<RBatis> = OnceLock::new();
#[derive(Debug, thiserror::Error)]
pub enum SetupError {
#[error("The provided database URL is invalid.")]
UrlError(#[from] rbatis::Error),
#[cfg(any(feature = "mysql", feature = "postgres"))]
#[error("The database pool has already been initialized.")]
AlreadyInitialized,
}
#[cfg(any(feature = "mysql", feature = "postgres"))]
pub fn setup(database_url: &str) -> Result<(), SetupError> {
let rb = RBatis::new();
#[cfg(feature = "mysql")]
tracing::info!(
database_url = database_url,
"Setting up MySQL database pool..."
);
#[cfg(feature = "postgres")]
tracing::info!(
database_url = database_url,
"Setting up PostgreSQL database pool..."
);
#[cfg(feature = "mysql")]
rb.init_option::<MysqlDriver, MySqlConnectOptions, DefaultPool>(
MysqlDriver {},
MySqlConnectOptions::from_str(database_url)?,
)?;
#[cfg(feature = "postgres")]
rb.init_option::<PgDriver, PgConnectOptions, DefaultPool>(
PgDriver {},
PgConnectOptions::from_str(database_url)?,
)?;
DB_POOL
.set(rb)
.map_err(|_| SetupError::AlreadyInitialized)?;
Ok(())
}
#[derive(Debug, thiserror::Error)]
pub enum ConnectError {
#[error("The database pool has not been initialized.")]
NotInitialized,
#[error("An error occurred while connecting to the database.")]
Connection(#[from] rbatis::Error),
}
pub async fn get() -> Result<Connection, ConnectError> {
match DB_POOL.get() {
None => Err(ConnectError::NotInitialized),
Some(rb) => Ok(rb.get_pool()?.get().await?),
}
}
pub enum Database {
MySQL,
PostgreSQL,
}
impl Database {
pub const fn is_mysql(&self) -> bool {
matches!(self, Self::MySQL)
}
pub const fn is_postgres(&self) -> bool {
matches!(self, Self::PostgreSQL)
}
}
pub const fn which_db() -> Database {
#[cfg(all(not(feature = "mysql"), not(feature = "postgres")))]
panic!("Either the `mysql` or `postgres` feature must be enabled to use `ensemble`.");
#[cfg(all(feature = "mysql", feature = "postgres"))]
panic!("Both the `mysql` and `postgres` features are enabled. Please enable only one of them.");
if cfg!(feature = "mysql") {
Database::MySQL
} else {
Database::PostgreSQL
}
}