use std::cmp::max;
use std::collections::HashMap;
use std::time::Instant;
use crate::*;
#[derive(Debug, Default)]
pub struct ValidateOptions {
pub skip_block_hashes: bool,
}
pub(crate) fn validate_bands(
archive: &Archive,
band_ids: &[BandId],
) -> (HashMap<BlockHash, u64>, ValidateStats) {
let mut stats = ValidateStats::default();
let mut block_lens = HashMap::new();
struct ProgressModel {
bands_done: usize,
bands_total: usize,
start: Instant,
}
impl nutmeg::Model for ProgressModel {
fn render(&mut self, _width: usize) -> String {
format!(
"Check index {}/{}, {} done, {} remaining",
self.bands_done,
self.bands_total,
nutmeg::percent_done(self.bands_done, self.bands_total),
nutmeg::estimate_remaining(&self.start, self.bands_done, self.bands_total)
)
}
}
let view = nutmeg::View::new(
ProgressModel {
start: Instant::now(),
bands_done: 0,
bands_total: band_ids.len(),
},
ui::nutmeg_options(),
);
for band_id in band_ids {
if let Ok(b) = Band::open(archive, band_id) {
if b.validate(&mut stats).is_err() {
stats.band_metadata_problems += 1;
}
} else {
stats.band_open_errors += 1;
continue;
}
if let Ok(st) = archive.open_stored_tree(BandSelectionPolicy::Specified(band_id.clone())) {
if let Ok((st_block_lens, st_stats)) = validate_stored_tree(&st) {
stats += st_stats;
for (bh, bl) in st_block_lens {
block_lens
.entry(bh)
.and_modify(|al| *al = max(*al, bl))
.or_insert(bl);
}
} else {
stats.tree_validate_errors += 1
}
} else {
stats.tree_open_errors += 1;
continue;
}
view.update(|model| model.bands_done += 1);
}
(block_lens, stats)
}
pub(crate) fn validate_stored_tree(
st: &StoredTree,
) -> Result<(HashMap<BlockHash, u64>, ValidateStats)> {
let mut block_lens = HashMap::new();
let stats = ValidateStats::default();
for entry in st
.iter_entries(Apath::root(), Exclude::nothing())?
.filter(|entry| entry.kind() == Kind::File)
{
for addr in entry.addrs {
let end = addr.start + addr.len;
block_lens
.entry(addr.hash.clone())
.and_modify(|l| *l = max(*l, end))
.or_insert(end);
}
}
Ok((block_lens, stats))
}