use std::path::Path;
use crate::delta::IndexDelta;
use crate::delta_sharded::ShardedDelta;
pub const EXCLUDED_FILES: &[&str] = &[
".lock",
".tantivy-writer.lock",
".lucivy-writer.lock",
".managed.json",
];
pub fn read_directory_files(path: &Path) -> Result<Vec<(String, Vec<u8>)>, String> {
let mut files = Vec::new();
let entries = std::fs::read_dir(path)
.map_err(|e| format!("cannot read directory '{}': {e}", path.display()))?;
for entry in entries {
let entry = entry.map_err(|e| format!("directory entry error: {e}"))?;
let ft = entry
.file_type()
.map_err(|e| format!("file type error: {e}"))?;
if ft.is_file() {
let name = entry.file_name().to_string_lossy().to_string();
if EXCLUDED_FILES.contains(&name.as_str()) {
continue;
}
let data = std::fs::read(entry.path())
.map_err(|e| format!("cannot read file '{}': {e}", entry.path().display()))?;
files.push((name, data));
}
}
Ok(files)
}
pub fn apply_delta(dest_path: &Path, delta: &IndexDelta) -> Result<(), String> {
for bundle in &delta.added_segments {
for (name, data) in &bundle.files {
let file_path = dest_path.join(name);
std::fs::write(&file_path, data)
.map_err(|e| format!("cannot write segment file '{}': {e}", file_path.display()))?;
}
}
if !delta.removed_segment_ids.is_empty() {
let entries = std::fs::read_dir(dest_path)
.map_err(|e| format!("cannot read directory '{}': {e}", dest_path.display()))?;
for entry in entries {
let entry = entry.map_err(|e| format!("dir entry error: {e}"))?;
let name = entry.file_name().to_string_lossy().to_string();
for removed_id in &delta.removed_segment_ids {
if name.starts_with(removed_id.as_str()) {
let _ = std::fs::remove_file(entry.path());
}
}
}
}
let meta_path = dest_path.join("meta.json");
let meta_tmp = dest_path.join("meta.json.tmp");
std::fs::write(&meta_tmp, &delta.meta)
.map_err(|e| format!("cannot write meta.json.tmp: {e}"))?;
std::fs::rename(&meta_tmp, &meta_path)
.map_err(|e| format!("cannot rename meta.json.tmp → meta.json: {e}"))?;
if let Some(config) = &delta.config {
std::fs::write(dest_path.join("_config.json"), config)
.map_err(|e| format!("cannot write _config.json: {e}"))?;
}
Ok(())
}
pub fn apply_sharded_delta(base_path: &Path, delta: &ShardedDelta) -> Result<(), String> {
if let Some(config) = &delta.shard_config {
std::fs::create_dir_all(base_path)
.map_err(|e| format!("cannot create base dir: {e}"))?;
std::fs::write(base_path.join("_shard_config.json"), config)
.map_err(|e| format!("cannot write _shard_config.json: {e}"))?;
}
for (shard_id, shard_delta) in &delta.shard_deltas {
let shard_dir = base_path.join(format!("shard_{shard_id}"));
std::fs::create_dir_all(&shard_dir)
.map_err(|e| format!("cannot create shard_{shard_id} dir: {e}"))?;
apply_delta(&shard_dir, shard_delta)?;
}
Ok(())
}
pub fn remove_lock_files(dir: &Path) {
if let Ok(entries) = std::fs::read_dir(dir) {
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if name.contains(".lock") {
let _ = std::fs::remove_file(entry.path());
}
}
}
}