use std::ffi::OsString;
use std::path::{Path, PathBuf};
use rusqlite::{Connection, OpenFlags};
use tempfile::TempDir;
use crate::Result;
pub(crate) struct Db {
pub conn: Connection,
_temp: Option<TempCopy>,
}
pub(crate) fn open(path: &Path) -> Result<Db> {
match Connection::open_with_flags(path, OpenFlags::SQLITE_OPEN_READ_ONLY) {
Ok(conn) => Ok(Db { conn, _temp: None }),
Err(_) => open_copy(path),
}
}
pub(crate) fn open_copy(path: &Path) -> Result<Db> {
let temp = TempCopy::of(path)?;
let conn = Connection::open(&temp.db)?;
Ok(Db { conn, _temp: Some(temp) })
}
struct TempCopy {
#[allow(dead_code, reason = "keeps the temporary directory alive until Drop")]
dir: TempDir,
db: PathBuf,
}
impl TempCopy {
fn of(path: &Path) -> Result<Self> {
let dir = tempfile::Builder::new().prefix("unjar-").tempdir()?;
let name = path.file_name().ok_or("invalid database path")?;
let db = dir.path().join(name);
std::fs::copy(path, &db)?;
for suffix in ["-wal", "-shm"] {
let src = with_suffix(path.as_os_str(), suffix);
if Path::new(&src).exists() {
std::fs::copy(&src, with_suffix(db.as_os_str(), suffix))?;
}
}
Ok(Self { dir, db })
}
}
fn with_suffix(base: &std::ffi::OsStr, suffix: &str) -> PathBuf {
let mut s = OsString::from(base);
s.push(suffix);
PathBuf::from(s)
}