use std::{env, error::Error, sync::LazyLock}; use rusqlite::{Connection, OptionalExtension}; macro_rules! migration { ($name:literal) => { ($name, include_str!(concat!("./migrations/", $name, ".sql"))) }; } const MIGRATIONS: &[(&str, &str)] = &[migration!("2026-02-20--01")]; pub static DB_URL: LazyLock = LazyLock::new(|| env::var("DATABASE_URL").expect("DATABASE_URL is not set")); const PERSISTENT_PRAGMAS: &[&str] = &["PRAGMA journal_mode = WAL"]; const CONNECTION_PRAGMAS: &[&str] = &["PRAGMA foreign_keys = ON", "PRAGMA busy_timeout = 5000"]; const TABLE_MIGRATIONS: &str = r#" CREATE TABLE IF NOT EXISTS migrations ( id TEXT PRIMARY KEY, time INTEGER DEFAULT (unixepoch()) ); "#; pub fn conn() -> Result { let conn = Connection::open(&*DB_URL)?; for pragma in CONNECTION_PRAGMAS { conn.query_row(*pragma, (), |_| Ok(())).optional()?; } Ok(conn) } pub fn migrations() -> Result<(), Box> { let conn = Connection::open(&*DB_URL)?; for pragma in PERSISTENT_PRAGMAS { conn.query_row(*pragma, (), |_| Ok(()))?; } conn.execute(TABLE_MIGRATIONS, ())?; let mut changes = false; for (key, sql) in MIGRATIONS { let mut statement = conn.prepare("SELECT id, time FROM migrations WHERE id = ?1")?; let query = statement.query_one([key], |_| Ok(())).optional()?; if query.is_some() { continue; } changes = true; println!("Applying migration {key}..."); conn.execute_batch(sql)?; conn.execute("INSERT INTO migrations(id) VALUES (?1)", &[key])?; } if changes { println!("Migrations applied.") } Ok(()) }