use sha2::{Digest, Sha256}; use std::{ fs::File, io::{self, Read, Write}, path::Path, }; use walkdir::WalkDir; pub fn hash_reader(name: &str, reader: &mut dyn Read) -> io::Result { let mut hasher = Sha256::new(); io::copy(reader, &mut hasher)?; Ok(format!("{} {}\n", hex::encode(hasher.finalize()), name)) } fn hash_file(fname: &Path, name: &Path) -> io::Result { let mut fin = File::open(name)?; hash_reader(fname.as_os_str().to_str().unwrap(), &mut fin) } pub fn hash_dir(name: &str) -> io::Result { let mut result = String::new(); for entry in WalkDir::new(name) .into_iter() .filter(Result::is_ok) .map(Result::unwrap) .filter(|dir_entry| !dir_entry.file_type().is_dir()) .map(|de| de.into_path()) .map(|de| hash_file(&de.strip_prefix(name).unwrap(), &de)) { result.push_str(&entry?); } let mut hasher = Sha256::new(); hasher.write_all(&result.as_bytes())?; Ok(format!("h1:{}", base64::encode(hasher.finalize()))) } #[cfg(test)] mod tests { use std::process::Command; #[test] fn hash_dir() { let hash = super::hash_dir("./testdata").unwrap(); assert_eq!("h1:nJxVO77692drULxxVJvOrMJE8fl8vG1NLjoDDv9e7+E=", &hash); } #[test] fn test_compare_go() { let rust_hash = super::hash_dir("./testdata").unwrap(); let go_output = Command::new("go") .arg("run") .arg("./go_cmp") .output() .expect("go to run"); let go_hash: String = String::from_utf8(go_output.stdout).expect("go output to be utf-8"); assert_eq!(go_hash, rust_hash); } }