proto_core 0.57.6

Core proto APIs.
Documentation
use super::checksum_error::ProtoChecksumError;
use starbase_utils::fs::{self, FsError};
use std::io::{BufRead, BufReader};
use std::path::Path;
use tracing::trace;
use warpgate::{hash_sha256, hash_sha512};

pub fn hash_file_contents_sha256<P: AsRef<Path>>(path: P) -> Result<String, FsError> {
    let path = path.as_ref();

    trace!(file = ?path, "Calculating SHA256 checksum");

    let bytes = fs::read_file_bytes(path)?;
    let hash = hash_sha256(bytes);

    trace!(file = ?path, hash, "Calculated hash");

    Ok(hash)
}

pub fn hash_file_contents_sha512<P: AsRef<Path>>(path: P) -> Result<String, FsError> {
    let path = path.as_ref();

    trace!(file = ?path, "Calculating SHA512 checksum");

    let bytes = fs::read_file_bytes(path)?;
    let hash = hash_sha512(bytes);

    trace!(file = ?path, hash, "Calculated hash");

    Ok(hash)
}

#[tracing::instrument(name = "verify_sha_checksum")]
pub fn verify_checksum(
    download_file: &Path,
    checksum_file: &Path,
    checksum_hash: &str,
) -> Result<bool, ProtoChecksumError> {
    let download_file_name = fs::file_name(download_file);

    for line in
        BufReader::new(
            fs::open_file(checksum_file).map_err(|error| ProtoChecksumError::Sha {
                error: Box::new(error),
            })?,
        )
        .lines()
        .map_while(Result::ok)
    {
        if line.is_empty() {
            continue;
        }

        // <checksum>  <file>
        // <checksum> *<file>
        // <checksum>
        if line == checksum_hash
            || (line.starts_with(checksum_hash) && line.ends_with(&download_file_name))
        {
            return Ok(true);
        }

        // Checksum files on Windows are created with Get-FileHash,
        // which has a different file structure than Unix
        // https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-filehash?view=powershell-7.5
        if line.starts_with("Hash")
            && let Some((_, hash)) = line.split_once(':')
        {
            // The hash is all uppercase in the checksum file,
            // but the one's we generate are not, so lowercase
            return Ok(hash.trim().to_lowercase() == checksum_hash);
        }
    }

    Ok(false)
}