use std::path::Path;
use std::time::Instant;
use sha2::{Digest, Sha256};
use crate::migration::legacy_reader::LegacyDatabase;
use crate::{Database, Options, Result};
#[derive(Debug, Clone)]
pub struct MigrationStats {
pub dictionary_entries: usize,
pub triples_migrated: usize,
pub properties_migrated: usize,
pub duration_secs: f64,
pub source_sha256: String,
pub target_sha256: String,
}
pub fn migrate_database<P: AsRef<Path>, Q: AsRef<Path>>(
source_path: P,
target_path: Q,
verify: bool,
) -> Result<MigrationStats> {
let start_time = Instant::now();
let mut legacy_db = LegacyDatabase::open(&source_path)?;
let source_sha256 = if verify {
calculate_file_sha256(&source_path)?
} else {
String::new()
};
let mut new_db = Database::open(Options::new(target_path.as_ref()))?;
let dictionary = legacy_db.dictionary();
let dictionary_entries = dictionary.len();
for (id, value) in dictionary.iter().enumerate() {
let new_id = new_db.intern(value)?;
if new_id != id as u64 {
return Err(crate::Error::Other(format!(
"Dictionary ID mismatch: expected {}, got {}",
id, new_id
)));
}
}
let triples = legacy_db.read_triples()?;
let triples_migrated = triples.len();
new_db.batch_insert(&triples)?;
let properties_data = legacy_db.read_properties()?;
let properties_migrated = if !properties_data.is_empty() {
0
} else {
0
};
let target_sha256 = if verify {
drop(new_db);
calculate_file_sha256(&target_path)?
} else {
String::new()
};
let duration_secs = start_time.elapsed().as_secs_f64();
Ok(MigrationStats {
dictionary_entries,
triples_migrated,
properties_migrated,
duration_secs,
source_sha256,
target_sha256,
})
}
fn calculate_file_sha256<P: AsRef<Path>>(path: P) -> Result<String> {
use std::fs::File;
use std::io::Read;
let mut file = File::open(path)?;
let mut hasher = Sha256::new();
let mut buffer = vec![0u8; 8192];
loop {
let n = file.read(&mut buffer)?;
if n == 0 {
break;
}
hasher.update(&buffer[..n]);
}
Ok(format!("{:x}", hasher.finalize()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sha256_calculation() {
use std::io::Write;
use tempfile::NamedTempFile;
let mut file = NamedTempFile::new().unwrap();
file.write_all(b"hello world").unwrap();
file.flush().unwrap();
let checksum = calculate_file_sha256(file.path()).unwrap();
assert_eq!(
checksum,
"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
);
}
}