use std::fs;
use std::path::Path;
use crate::storage::error::StorageResult;
use walletkit_db::cipher;
use walletkit_db::Connection;
use zeroize::Zeroizing;
use super::schema;
use super::util::{map_db_err, map_io_err};
pub(super) fn open_or_rebuild(
path: &Path,
k_intermediate: &Zeroizing<[u8; 32]>,
) -> StorageResult<Connection> {
match open_prepared(path, k_intermediate) {
Ok(conn) => {
let integrity_ok =
cipher::integrity_check(&conn).map_err(|e| map_db_err(&e))?;
if integrity_ok {
Ok(conn)
} else {
drop(conn);
rebuild(path, k_intermediate)
}
}
Err(err) => rebuild(path, k_intermediate).map_or_else(|_| Err(err), Ok),
}
}
fn open_prepared(
path: &Path,
k_intermediate: &Zeroizing<[u8; 32]>,
) -> StorageResult<Connection> {
let conn = cipher::open_encrypted(path, k_intermediate, false)
.map_err(|e| map_db_err(&e))?;
schema::ensure_schema(&conn)?;
Ok(conn)
}
fn rebuild(
path: &Path,
k_intermediate: &Zeroizing<[u8; 32]>,
) -> StorageResult<Connection> {
delete_cache_files(path)?;
open_prepared(path, k_intermediate)
}
fn delete_cache_files(path: &Path) -> StorageResult<()> {
delete_if_exists(path)?;
delete_if_exists(&path.with_extension("sqlite-wal"))?;
delete_if_exists(&path.with_extension("sqlite-shm"))?;
Ok(())
}
fn delete_if_exists(path: &Path) -> StorageResult<()> {
match fs::remove_file(path) {
Ok(()) => Ok(()),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(()),
Err(err) => Err(map_io_err(&err)),
}
}