use crate::error::Error;
use sqlx::migrate::Migrator;
pub use sqlx::sqlite::{SqliteConnectOptions, SqlitePool};
use sqlx::sqlite::{SqlitePoolOptions, SqliteSynchronous};
use sqlx::ConnectOptions;
use std::time::Duration;
use tracing::{error, info, warn};
static MIGRATOR: Migrator = sqlx::migrate!();
async fn get_file_pool(
filename: &std::path::Path,
db_connections: u32,
timeout: Duration,
) -> Result<SqlitePool, Error> {
let slow_log = Duration::from_secs_f32(0.3);
info!(
"Opening SQLite database file: {}, connections={}, timeout={}",
filename.display(),
db_connections,
timeout.as_secs_f32()
);
let conn = SqliteConnectOptions::new()
.filename(filename)
.synchronous(SqliteSynchronous::Normal)
.shared_cache(false)
.pragma("temp_store", "memory")
.log_statements(log::LevelFilter::Trace)
.log_slow_statements(log::LevelFilter::Warn, slow_log);
let pool = SqlitePoolOptions::new()
.min_connections(db_connections)
.max_connections(db_connections)
.test_before_acquire(false)
.acquire_timeout(timeout)
.connect_with(conn)
.await?;
Ok(pool)
}
pub async fn run_migrations(pool: &SqlitePool) -> Result<(), Error> {
MIGRATOR.run(pool).await?;
Ok(())
}
pub struct SqlitePoolBuilder<'tempfile> {
path: Option<&'tempfile std::path::Path>,
migrate: bool,
db_connections: u32,
timeout: Duration,
}
impl<'tempfile> SqlitePoolBuilder<'tempfile> {
#[must_use]
pub const fn new() -> Self {
Self {
path: None,
migrate: true,
db_connections: 4,
timeout: Duration::from_secs(5),
}
}
#[must_use]
pub const fn db_path(mut self, path: &'tempfile std::path::Path) -> Self {
self.path = Some(path);
self
}
#[must_use]
pub const fn db_connections(mut self, db_connections: Option<u32>) -> Self {
if let Some(conns) = db_connections {
self.db_connections = conns;
}
self
}
#[must_use]
pub const fn db_timeout(mut self, db_timeout: Option<u32>) -> Self {
if let Some(timeout) = db_timeout {
assert!(
timeout <= 25,
"DBus timeout is ~25s. timeout should be less."
);
self.timeout = Duration::from_secs(timeout as u64);
}
self
}
#[must_use]
pub const fn migrate(mut self, migrate: bool) -> Self {
self.migrate = migrate;
self
}
pub async fn build(self) -> Result<SqlitePool, Error> {
let db_path = self.path.expect("Must have a path");
if self.db_connections < 2 {
error!(
"Too few connections to function. conns = {} < 2",
self.db_connections
);
}
let pool = get_file_pool(db_path, self.db_connections, self.timeout).await?;
if self.migrate {
warn!("Running migrations on {:?}", &db_path);
run_migrations(&pool).await?;
}
Ok(pool)
}
}
impl Default for SqlitePoolBuilder<'_> {
fn default() -> Self {
Self::new()
}
}