1use crate::manifest::BackupManifest;
2use powdb_storage::catalog::Catalog;
3use std::io;
4use std::path::Path;
5
6pub(crate) fn ensure_empty_dir(dest_data_dir: &Path) -> io::Result<()> {
10 if dest_data_dir.exists() && dest_data_dir.read_dir()?.next().is_some() {
11 return Err(io::Error::other(format!(
12 "restore destination {} is not empty; restore requires a fresh or empty directory",
13 dest_data_dir.display()
14 )));
15 }
16 std::fs::create_dir_all(dest_data_dir)?;
17 Ok(())
18}
19
20pub(crate) fn verify_and_copy_full(
24 manifest: &BackupManifest,
25 backup_dir: &Path,
26 dest_data_dir: &Path,
27) -> io::Result<()> {
28 for f in &manifest.files {
29 let bytes = std::fs::read(backup_dir.join(&f.name))?;
30 let hash = blake3::hash(&bytes).to_hex().to_string();
31 if hash != f.blake3_hex {
32 return Err(io::Error::other(format!(
33 "integrity check failed for {}: blake3 mismatch (backup is corrupt)",
34 f.name
35 )));
36 }
37 std::fs::write(dest_data_dir.join(&f.name), &bytes)?;
38 }
39 Ok(())
40}
41
42pub fn restore(backup_dir: &Path, dest_data_dir: &Path) -> io::Result<()> {
47 let manifest = BackupManifest::read(backup_dir)?;
48 ensure_empty_dir(dest_data_dir)?;
49 verify_and_copy_full(&manifest, backup_dir, dest_data_dir)?;
50 let cat = Catalog::open(dest_data_dir)?;
52 drop(cat);
53 Ok(())
54}