use std::{fs, hash::DefaultHasher, path::Path};
use std::hash::Hasher as RustHasher;
use tracing::{debug, error, warn};
#[cfg(feature = "sha")]
pub mod sha;
#[cfg(feature = "md5")]
pub mod md5;
pub trait Hasher {
fn compute(bytes: &[u8]) -> Result<String, String>
where
Self: Sized;
}
impl Hasher for std::hash::DefaultHasher {
fn compute(bytes: &[u8]) -> Result<String, String> {
let mut hasher = DefaultHasher::new();
hasher.write(bytes);
let hash = hasher.finish();
Ok(hex::encode(hash.to_be_bytes()))
}
}
#[derive(Debug, Clone)]
pub struct Hash {
expect: String,
hasher: fn(&[u8]) -> Result<String, String>,
}
impl Hash {
pub fn new<T: Hasher + 'static>(expect: &str) -> Self {
Self {
expect: expect.to_string(),
hasher: T::compute,
}
}
pub fn check_bytes(&self, bytes: Vec<u8>) -> Option<()> {
let hash = (self.hasher)(&bytes);
let hash = match hash {
Result::Err(e) => {
error!("Failed computing hash -- {e}");
return None;
}
Result::Ok(hash) => hash,
};
debug!("HASH: {}", hash);
if self.expect.ne(&hash) {
warn!("Hash don't match");
return None;
}
Some(())
}
pub fn check_file<P: AsRef<Path>>(&self, file: P) -> std::io::Result<Option<()>> {
let bytes = fs::read(file.as_ref())?;
Ok(self.check_bytes(bytes))
}
}