use sqlx::{PgPool, postgres::PgPoolOptions};
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct DbPoolConfig {
pub url: String,
pub max_connections: u32,
pub min_connections: u32,
pub idle_timeout: Duration,
pub max_lifetime: Duration,
}
impl Default for DbPoolConfig {
fn default() -> Self {
Self {
url: std::env::var("DATABASE_URL")
.unwrap_or_else(|_| "postgres://localhost:5432/kegani".to_string()),
max_connections: 10,
min_connections: 2,
idle_timeout: Duration::from_secs(600),
max_lifetime: Duration::from_secs(3600),
}
}
}
impl DbPoolConfig {
pub fn new(url: &str) -> Self {
Self {
url: url.to_string(),
..Default::default()
}
}
pub fn max_connections(mut self, n: u32) -> Self {
self.max_connections = n;
self
}
pub fn min_connections(mut self, n: u32) -> Self {
self.min_connections = n;
self
}
pub fn idle_timeout(mut self, d: Duration) -> Self {
self.idle_timeout = d;
self
}
}
#[derive(Clone)]
pub struct DbPool {
inner: PgPool,
}
impl DbPool {
pub async fn new(config: DbPoolConfig) -> Result<Self, sqlx::Error> {
let pool = PgPoolOptions::new()
.max_connections(config.max_connections)
.min_connections(config.min_connections)
.acquire_timeout(Duration::from_secs(5))
.idle_timeout(config.idle_timeout)
.max_lifetime(config.max_lifetime)
.connect(&config.url)
.await?;
Ok(Self { inner: pool })
}
pub async fn connect(url: &str) -> Result<Self, sqlx::Error> {
Self::new(DbPoolConfig::new(url)).await
}
pub fn inner(&self) -> &PgPool {
&self.inner
}
pub async fn ping(&self) -> Result<(), sqlx::Error> {
sqlx::query("SELECT 1")
.fetch_one(&self.inner)
.await
.map(|_| ())
}
pub async fn begin(&self) -> Result<Transaction, sqlx::Error> {
self.inner
.begin()
.await
.map(|tx| Transaction { inner: Some(tx) })
}
}
impl std::ops::Deref for DbPool {
type Target = PgPool;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
pub struct Transaction {
inner: Option<sqlx::Transaction<'static, sqlx::Postgres>>,
}
impl Transaction {
pub async fn commit(self) -> Result<(), sqlx::Error> {
if let Some(tx) = self.inner {
tx.commit().await
} else {
Ok(())
}
}
pub async fn rollback(self) -> Result<(), sqlx::Error> {
if let Some(tx) = self.inner {
tx.rollback().await
} else {
Ok(())
}
}
}