ripr 0.8.0

Find static mutation-exposure gaps before expensive mutation testing
Documentation
use sha2::{Digest, Sha256};
use std::path::Path;

pub(crate) fn sha256_file(path: &Path) -> Result<String, String> {
    let bytes = std::fs::read(path)
        .map_err(|err| format!("read artifact {} failed: {err}", path.display()))?;
    let mut hasher = Sha256::new();
    hasher.update(bytes);
    let digest = hasher.finalize();
    let mut rendered = String::from("sha256:");
    for byte in digest {
        rendered.push_str(&format!("{byte:02x}"));
    }
    Ok(rendered)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::time::{SystemTime, UNIX_EPOCH};

    fn unique_provenance_test_dir(name: &str) -> Result<std::path::PathBuf, String> {
        let stamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map_err(|err| format!("clock before epoch: {err}"))?
            .as_nanos();
        let dir = std::env::temp_dir().join(format!("ripr-{name}-{stamp}"));
        std::fs::create_dir_all(&dir).map_err(|err| format!("create temp dir: {err}"))?;
        Ok(dir)
    }

    #[test]
    fn sha256_file_hashes_artifact_bytes() -> Result<(), String> {
        let dir = unique_provenance_test_dir("sha256")?;
        let path = dir.join("artifact.json");
        std::fs::write(&path, "abc\n").map_err(|err| format!("write artifact: {err}"))?;

        assert_eq!(
            sha256_file(&path)?,
            "sha256:edeaaff3f1774ad2888673770c6d64097e391bc362d7d6fb34982ddf0efd18cb"
        );

        std::fs::remove_dir_all(&dir).map_err(|err| format!("remove temp dir: {err}"))?;
        Ok(())
    }
}