use crate::error::DbError;
use super::connection::PgConnection;
use super::tls::make_rustls_config;
use deadpool_postgres::{Manager, ManagerConfig, Pool, RecyclingMethod, Runtime};
#[derive(Debug, Clone)]
pub struct PgConfig {
pub host: String,
pub port: u16,
pub user: String,
pub password: String,
pub dbname: String,
pub max_connections: usize,
pub ssl: bool,
}
impl PgConfig {
#[must_use]
pub fn for_test(dbname: impl Into<String>) -> Self {
Self {
host: std::env::var("TEST_DB_HOST").unwrap_or_else(|_| "localhost".into()),
port: std::env::var("TEST_DB_PORT")
.ok()
.and_then(|p| p.parse().ok())
.unwrap_or(5433),
user: std::env::var("TEST_DB_USER").unwrap_or_else(|_| "toolu".into()),
password: std::env::var("TEST_DB_PASSWORD").unwrap_or_else(|_| "toolu".into()),
dbname: dbname.into(),
max_connections: 5,
ssl: false,
}
}
}
#[derive(Clone)]
pub struct PgDatabase {
pool: Pool,
}
impl PgDatabase {
pub async fn init(config: &PgConfig) -> Result<Self, DbError> {
let mut pg_config = tokio_postgres::Config::new();
pg_config
.host(&config.host)
.port(config.port)
.user(&config.user)
.password(&config.password)
.dbname(&config.dbname);
let mgr_config = ManagerConfig {
recycling_method: RecyclingMethod::Fast,
};
let pool = if config.ssl {
let tls_config = make_rustls_config()?;
let tls = tokio_postgres_rustls::MakeRustlsConnect::new(tls_config);
let mgr = Manager::from_config(pg_config, tls, mgr_config);
Pool::builder(mgr)
.max_size(config.max_connections)
.runtime(Runtime::Tokio1)
.build()
.map_err(|e| DbError::Pool(format!("pool creation failed: {e}")))?
} else {
let mgr = Manager::from_config(pg_config, tokio_postgres::NoTls, mgr_config);
Pool::builder(mgr)
.max_size(config.max_connections)
.runtime(Runtime::Tokio1)
.build()
.map_err(|e| DbError::Pool(format!("pool creation failed: {e}")))?
};
let _client = pool
.get()
.await
.map_err(|e| DbError::Connection(format!("initial connection failed: {e}")))?;
tracing::info!(
host = %config.host,
port = config.port,
dbname = %config.dbname,
ssl = config.ssl,
max_connections = config.max_connections,
"postgres connection pool initialized"
);
Ok(Self { pool })
}
pub async fn connect(&self) -> Result<PgConnection, DbError> {
let client = self
.pool
.get()
.await
.map_err(|e| DbError::Pool(format!("pool checkout failed: {e}")))?;
Ok(PgConnection { client })
}
}