use std::fs;
use std::path::Path;
use crate::branch::refstore::{
REF_EXTENSION, TEMP_PREFIX, TEMP_SUFFIX, decode_record, ref_file_name,
};
use crate::branch::snapshot::decode_registry;
use crate::branch::{BranchRefRecord, SnapshotEntry};
use super::error::VacuumError;
#[derive(Debug)]
pub struct RefsDirScan {
pub records: Vec<BranchRefRecord>,
pub temp_debris_files: usize,
pub foreign_entries: usize,
}
pub fn read_refs_dir(dir: &Path) -> Result<RefsDirScan, VacuumError> {
let mut scan = RefsDirScan {
records: Vec::new(),
temp_debris_files: 0,
foreign_entries: 0,
};
let entries = fs::read_dir(dir).map_err(|error| VacuumError::Io {
path: dir.to_path_buf(),
error,
})?;
for entry in entries {
let entry = entry.map_err(|error| VacuumError::Io {
path: dir.to_path_buf(),
error,
})?;
let path = entry.path();
let Some(file_name) = path.file_name().and_then(|name| name.to_str()) else {
scan.foreign_entries += 1;
continue;
};
if file_name.starts_with(TEMP_PREFIX) && file_name.ends_with(TEMP_SUFFIX) {
scan.temp_debris_files += 1;
continue;
}
if path.extension().and_then(|ext| ext.to_str()) != Some(REF_EXTENSION) {
scan.foreign_entries += 1;
continue;
}
let bytes = fs::read(&path).map_err(|error| VacuumError::Io {
path: path.clone(),
error,
})?;
let record = decode_record(&bytes).map_err(|error| VacuumError::MetadataOpen {
path: path.clone(),
reason: error.to_string(),
})?;
if ref_file_name(&record.name) != file_name {
return Err(VacuumError::MetadataOpen {
path,
reason: format!(
"ref file holds a record for branch {:?}, whose name hashes elsewhere",
record.name
),
});
}
scan.records.push(record);
}
Ok(scan)
}
pub fn read_snapshot_registry(path: &Path) -> Result<Vec<SnapshotEntry>, VacuumError> {
let bytes = fs::read(path).map_err(|error| VacuumError::Io {
path: path.to_path_buf(),
error,
})?;
decode_registry(&bytes).map_err(|error| VacuumError::MetadataOpen {
path: path.to_path_buf(),
reason: error.to_string(),
})
}