use crc32fast::Hasher;
use failure::*;
use filebuffer::FileBuffer;
#[cfg(feature = "progress")]
use indicatif::*;
#[cfg(feature = "progress")]
use lazy_static::lazy_static;
use rayon::prelude::*;
use std::path::Path;
use walkdir::WalkDir;
#[cfg(feature = "progress")]
lazy_static! {
pub static ref PROGRESS_BAR: ProgressBar = {
let progbar = ProgressBar::new(0);
progbar.set_style(
ProgressStyle::default_bar()
.template("[{elapsed_precise}] [{bar:40.green/blue}] {pos}/{len} ({eta})"),
);
progbar
};
}
#[cfg(feature = "progress")]
pub fn progress_init(length: u64) {
PROGRESS_BAR.set_length(length);
PROGRESS_BAR.set_draw_delta(length / 1000);
PROGRESS_BAR.set_position(0);
}
pub fn hash_file<P: AsRef<Path>>(hasher: &mut Hasher, root: &Path, path: P) -> Result<(), Error> {
let fbuffer = FileBuffer::open(&path)?;
let len = fbuffer.len();
fbuffer.prefetch(0, len);
hasher.update(
path.as_ref()
.strip_prefix(root)?
.to_str()
.ok_or(format_err!(
"path couldn't be read as str: {:?}",
path.as_ref()
))?
.as_bytes(),
);
hasher.update(&fbuffer);
Ok(())
}
pub fn hash_file_oneshot<P: AsRef<Path>>(root: &Path, path: P) -> Result<u32, Error> {
let mut hasher = Hasher::new();
hash_file(&mut hasher, root, path)?;
Ok(hasher.finalize())
}
pub fn sum_dir<P: AsRef<Path> + Send + Sync>(rootdir: P) -> Result<u32, Error> {
WalkDir::new(&rootdir)
.into_iter()
.filter_map(Result::ok)
.filter(|d| d.file_type().is_file())
.par_bridge()
.map(move |e| hash_file_oneshot(rootdir.as_ref(), e.path()))
.reduce(|| Ok(0u32), |acc, i| Ok(acc?.wrapping_add(i?)))
}
#[cfg(feature = "progress")]
pub fn sum_dir_prog<P: AsRef<Path> + Send + Sync>(rootdir: P) -> Result<u32, Error> {
let vec: Vec<_> = WalkDir::new(&rootdir)
.into_iter()
.filter_map(Result::ok)
.filter(|d| d.file_type().is_file())
.collect();
progress_init(vec.len() as u64);
vec.into_par_iter()
.map(move |e| {
let hash = hash_file_oneshot(rootdir.as_ref(), e.path());
PROGRESS_BAR.inc(1);
hash
})
.reduce(|| Ok(0u32), |acc, i| Ok(acc?.wrapping_add(i?)))
}
pub fn hash_dir<P: AsRef<Path>>(rootdir: P) -> Result<u32, Error> {
let mut hasher = Hasher::new();
let vec: Vec<_> = WalkDir::new(&rootdir)
.into_iter()
.filter_map(Result::ok)
.filter(|d| d.file_type().is_file())
.collect();
for entry in vec.into_iter() {
hash_file(&mut hasher, rootdir.as_ref(), entry.into_path())?;
}
Ok(hasher.finalize())
}
#[cfg(feature = "progress")]
pub fn hash_dir_prog<P: AsRef<Path>>(rootdir: P) -> Result<u32, Error> {
let mut hasher = Hasher::new();
let vec: Vec<_> = WalkDir::new(&rootdir)
.into_iter()
.filter_map(Result::ok)
.filter(|d| d.file_type().is_file())
.collect();
progress_init(vec.len() as u64);
for entry in vec.into_iter() {
let hash = hash_file(&mut hasher, rootdir.as_ref(), entry.into_path())?;
PROGRESS_BAR.inc(1);
hash
}
Ok(hasher.finalize())
}