use sqlx::postgres::{PgPool, PgPoolOptions};
use std::time::Duration;
use super::DbError;
#[derive(Clone)]
pub struct DatabasePool {
pool: PgPool,
}
#[derive(Debug, Clone)]
pub struct DatabaseConfig {
pub url: String,
pub max_connections: u32,
pub min_connections: u32,
pub connect_timeout_secs: u64,
pub idle_timeout_secs: u64,
}
impl Default for DatabaseConfig {
fn default() -> Self {
Self {
url: std::env::var("DATABASE_URL")
.unwrap_or_else(|_| "postgres://localhost/reasonkit".to_string()),
max_connections: 10,
min_connections: 2,
connect_timeout_secs: 30,
idle_timeout_secs: 600,
}
}
}
impl DatabaseConfig {
pub fn from_env() -> Self {
Self {
url: std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"),
max_connections: std::env::var("DB_MAX_CONNECTIONS")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(10),
min_connections: std::env::var("DB_MIN_CONNECTIONS")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(2),
connect_timeout_secs: std::env::var("DB_CONNECT_TIMEOUT")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(30),
idle_timeout_secs: std::env::var("DB_IDLE_TIMEOUT")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(600),
}
}
}
impl DatabasePool {
pub async fn new(config: &DatabaseConfig) -> Result<Self, DbError> {
let pool = PgPoolOptions::new()
.max_connections(config.max_connections)
.min_connections(config.min_connections)
.acquire_timeout(Duration::from_secs(config.connect_timeout_secs))
.idle_timeout(Duration::from_secs(config.idle_timeout_secs))
.connect(&config.url)
.await
.map_err(|e| DbError::ConnectionError(e.to_string()))?;
Ok(Self { pool })
}
pub fn pool(&self) -> &PgPool {
&self.pool
}
pub async fn run_migrations(&self) -> Result<(), DbError> {
sqlx::migrate!("src/portal/db/migrations")
.run(&self.pool)
.await
.map_err(|e| DbError::MigrationError(e.to_string()))
}
pub async fn health_check(&self) -> Result<(), DbError> {
sqlx::query("SELECT 1")
.execute(&self.pool)
.await
.map_err(|e| DbError::ConnectionError(e.to_string()))?;
Ok(())
}
pub async fn close(&self) {
self.pool.close().await;
}
}