use std::fs::File;
use std::io::Read;
use std::path::Path;
use sha2::{Digest, Sha256};
use crate::defaults::BYTES_PER_MIB;
use crate::lifecycle::util::hex_lower;
use crate::lifecycle::ModelError;
use super::COPY_BUFFER_BYTES;
const INSPECTION_PREFIX_BYTES: usize = 8 * BYTES_PER_MIB;
pub(super) fn inspect_local_path(source_path: &Path) -> Result<(String, Vec<u8>), ModelError> {
let mut source = File::open(source_path)?;
let prefix_capacity = source
.metadata()
.ok()
.map(|metadata| inspection_prefix_capacity(metadata.len()))
.unwrap_or(INSPECTION_PREFIX_BYTES);
hash_reader(&mut source, prefix_capacity)
}
fn hash_reader(
source: &mut impl Read,
prefix_limit: usize,
) -> Result<(String, Vec<u8>), ModelError> {
let mut hasher = Sha256::new();
let mut prefix = Vec::with_capacity(prefix_limit);
let mut buffer = vec![0u8; COPY_BUFFER_BYTES];
loop {
let read = source.read(&mut buffer)?;
if read == 0 {
break;
}
hasher.update(&buffer[..read]);
append_prefix_bytes(&mut prefix, &buffer[..read], prefix_limit);
}
Ok((hex_lower(&hasher.finalize()), prefix))
}
fn append_prefix_bytes(prefix: &mut Vec<u8>, bytes: &[u8], prefix_limit: usize) {
if prefix.len() >= prefix_limit {
return;
}
let remaining = prefix_limit - prefix.len();
prefix.extend_from_slice(&bytes[..bytes.len().min(remaining)]);
}
fn inspection_prefix_capacity(source_len: u64) -> usize {
usize::try_from(source_len)
.ok()
.map_or(INSPECTION_PREFIX_BYTES, |len| {
len.min(INSPECTION_PREFIX_BYTES)
})
}
pub(crate) fn hash_file(source_path: &Path) -> Result<String, ModelError> {
let mut source = File::open(source_path)?;
hash_reader(&mut source, 0).map(|(hash, _)| hash)
}
#[cfg(test)]
#[path = "../../tests/lifecycle/storage/content_tests.rs"]
mod content_tests;