hdiff-update-core 0.1.0

Core library for signed HDiffPatch-based differential application updates.
Documentation
use std::{
    fs::File,
    io::{BufReader, Read},
    path::{Path, PathBuf},
};

use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};

use crate::{error::io_path, Result};

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileDigest {
    pub path: PathBuf,
    pub sha256: String,
    pub size: u64,
}

pub fn sha256_file(path: impl AsRef<Path>) -> Result<FileDigest> {
    let path = path.as_ref();
    let file = File::open(path).map_err(|error| io_path(path, error))?;
    let size = file.metadata().map_err(|error| io_path(path, error))?.len();
    let mut reader = BufReader::with_capacity(1024 * 1024, file);
    let mut hasher = Sha256::new();
    let mut buf = vec![0_u8; 1024 * 1024];

    loop {
        let read = reader
            .read(&mut buf)
            .map_err(|error| io_path(path, error))?;
        if read == 0 {
            break;
        }
        hasher.update(&buf[..read]);
    }

    Ok(FileDigest {
        path: path.to_path_buf(),
        sha256: hex::encode(hasher.finalize()),
        size,
    })
}

pub fn sha256_bytes(bytes: &[u8]) -> String {
    let mut hasher = Sha256::new();
    hasher.update(bytes);
    hex::encode(hasher.finalize())
}

pub fn verify_sha256(path: impl AsRef<Path>, expected: &str) -> Result<FileDigest> {
    let digest = sha256_file(path.as_ref())?;
    if !digest.sha256.eq_ignore_ascii_case(expected) {
        return Err(crate::Error::HashMismatch {
            path: path.as_ref().to_path_buf(),
            expected: expected.to_string(),
            actual: digest.sha256,
        });
    }
    Ok(digest)
}