use std::path::{Path, PathBuf};
pub fn sif_path(sif_dir: &Path, reference: &str) -> PathBuf {
let safe: String = reference
.chars()
.map(|c| {
if c.is_alphanumeric() || c == '.' || c == '-' {
c
} else {
'_'
}
})
.collect();
sif_dir.join(format!("{safe}.sif"))
}
pub fn sif_path_for_digest(sif_dir: &Path, digest: &str) -> PathBuf {
let safe: String = digest
.chars()
.map(|c| if c.is_alphanumeric() { c } else { '_' })
.collect();
sif_dir.join(format!("{safe}.sif"))
}
pub fn file_sha256(path: &Path) -> std::io::Result<String> {
use std::fs::File;
use std::io::{BufReader, Read};
use sha2::{Digest, Sha256};
let file = File::open(path)?;
let mut reader = BufReader::with_capacity(64 * 1024, file);
let mut hasher = Sha256::new();
let mut buf = [0u8; 64 * 1024];
loop {
let n = reader.read(&mut buf)?;
if n == 0 {
break;
}
hasher.update(&buf[..n]);
}
let hash = hasher.finalize();
let hex: String = hash.iter().map(|b| format!("{b:02x}")).collect();
Ok(format!("sha256:{hex}"))
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
#[test]
fn file_sha256_streams_known_value() {
let dir = std::env::temp_dir().join(format!("bv_cache_test_{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("hello.txt");
{
let mut f = std::fs::File::create(&path).unwrap();
f.write_all(b"hello world").unwrap();
}
let got = file_sha256(&path).unwrap();
assert_eq!(
got,
"sha256:b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
);
let _ = std::fs::remove_file(&path);
let _ = std::fs::remove_dir(&dir);
}
#[test]
fn file_sha256_handles_chunk_boundary() {
let dir = std::env::temp_dir().join(format!("bv_cache_test_chunk_{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("big.bin");
{
let mut f = std::fs::File::create(&path).unwrap();
let chunk = vec![0xABu8; 70 * 1024];
f.write_all(&chunk).unwrap();
}
let got = file_sha256(&path).unwrap();
assert!(got.starts_with("sha256:"));
assert_eq!(got.len(), "sha256:".len() + 64);
let _ = std::fs::remove_file(&path);
let _ = std::fs::remove_dir(&dir);
}
}