use anyhow::{Context, Result};
use sha2::{Digest, Sha256};
use std::path::Path;
pub fn hash_file(path: &Path) -> Result<String> {
let content = std::fs::read(path)
.with_context(|| format!("failed to read file for hashing: {}", path.display()))?;
Ok(hash_content(&content))
}
fn hex_encode(bytes: &[u8]) -> String {
bytes
.iter()
.fold(String::with_capacity(bytes.len() * 2), |mut s, b| {
use std::fmt::Write;
write!(s, "{b:02x}").unwrap();
s
})
}
pub fn hash_content(content: &[u8]) -> String {
let mut hasher = Sha256::new();
hasher.update(content);
hex_encode(&hasher.finalize())
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn hash_file_returns_consistent_sha256() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("test.txt");
std::fs::write(&path, "hello world").unwrap();
let hash1 = hash_file(&path).unwrap();
let hash2 = hash_file(&path).unwrap();
assert_eq!(hash1, hash2);
assert_eq!(
hash1,
"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
);
}
#[test]
fn hash_content_matches_hash_file() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("test.txt");
let content = "some content";
std::fs::write(&path, content).unwrap();
assert_eq!(hash_file(&path).unwrap(), hash_content(content.as_bytes()));
}
}