use std::time::Duration; use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; use sqlx::{ConnectOptions, PgPool}; use crate::error::Result; pub const DEFAULT_URL: &str = "postgres:///scroll"; #[derive(Debug, Clone)] pub struct DatabaseConfig { pub url: String, pub max_connections: u32, pub acquire_timeout: Duration, pub statement_log_threshold: Duration, } impl Default for DatabaseConfig { fn default() -> Self { Self::new(DEFAULT_URL) } } impl DatabaseConfig { pub fn new(url: impl Into) -> Self { Self { url: url.into(), max_connections: 8, acquire_timeout: Duration::from_secs(5), statement_log_threshold: Duration::from_millis(250), } } pub fn from_env() -> Option { let url = std::env::var("DATABASE_URL") .ok() .filter(|value| !value.trim().is_empty()) .unwrap_or_else(|| DEFAULT_URL.to_owned()); let mut config = Self::new(url); if let Some(max) = std::env::var("DATABASE_MAX_CONNECTIONS") .ok() .and_then(|value| value.parse().ok()) { config.max_connections = max; } Some(config) } } #[derive(Debug, Clone)] pub struct Database { pool: PgPool, } impl Database { pub async fn connect(config: &DatabaseConfig) -> Result { let options: PgConnectOptions = config.url.parse::()?.log_slow_statements( tracing::log::LevelFilter::Warn, config.statement_log_threshold, ); let pool = PgPoolOptions::new() .max_connections(config.max_connections) .acquire_timeout(config.acquire_timeout) .connect_with(options) .await?; Ok(Self { pool }) } pub async fn migrate(&self) -> Result<()> { sqlx::migrate!("./migrations").run(&self.pool).await?; Ok(()) } pub fn pool(&self) -> &PgPool { &self.pool } pub async fn server_version(&self) -> Result { let version: String = sqlx::query_scalar("select version()") .fetch_one(&self.pool) .await?; Ok(version) } }