use sqlx::SqlitePool;
use std::path::Path;
pub async fn vacuum_into(pool: &SqlitePool, dest: &Path) -> anyhow::Result<()> {
let dest_str = dest
.to_str()
.ok_or_else(|| anyhow::anyhow!("backup destination path is not valid UTF-8"))?;
sqlx::query("VACUUM INTO ?1")
.bind(dest_str)
.execute(pool)
.await
.map_err(|e| anyhow::anyhow!("VACUUM INTO failed: {e}"))?;
Ok(())
}
pub fn snapshot_filename(epoch_secs: u64) -> String {
format!("oxipage-{epoch_secs}.db")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn filename_is_deterministic() {
assert_eq!(snapshot_filename(1735689600), "oxipage-1735689600.db");
}
#[tokio::test]
async fn vacuum_into_creates_file() {
let dir = std::env::temp_dir().join(format!("oxipage-backup-test-{}", std::process::id()));
tokio::fs::create_dir_all(&dir).await.unwrap();
let src = dir.join("src.db");
let dest = dir.join("snap.db");
let pool = crate::db::connect(&src).await.unwrap();
sqlx::query("CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)")
.execute(&pool)
.await
.unwrap();
sqlx::query("INSERT INTO t (v) VALUES ('hello')")
.execute(&pool)
.await
.unwrap();
vacuum_into(&pool, &dest).await.unwrap();
assert!(dest.exists(), "VACUUM INTO must create the snapshot file");
let meta = tokio::fs::metadata(&dest).await.unwrap();
assert!(meta.len() > 0);
let _ = tokio::fs::remove_dir_all(&dir).await;
}
}