doctor-diff-core 0.1.0

Tool for creating and applying compressed archives of changes between two directory workspaces
Documentation
use crate::hash::HashValue;
use ring::digest::{Context, SHA256};
use std::{
    collections::HashMap,
    fs::{read_dir, File},
    io::Read,
    io::Result,
    path::{Path, PathBuf},
};

pub fn hash_directory<P>(path: P) -> Result<HashMap<PathBuf, HashValue>>
where
    P: AsRef<Path>,
{
    let mut result = HashMap::new();
    hash_directory_inner(Path::new("").as_ref(), path.as_ref(), &mut result)?;
    Ok(result)
}

fn hash_directory_inner<P>(root: P, path: P, result: &mut HashMap<PathBuf, HashValue>) -> Result<()>
where
    P: AsRef<Path>,
{
    let root = root.as_ref();
    let path = path.as_ref();
    if path.is_dir() {
        for entry in read_dir(path)? {
            let entry = entry?;
            let root = root.join(entry.file_name());
            let path = path.join(entry.file_name());
            if path.is_dir() {
                hash_directory_inner(root, path, result)?;
            } else {
                let hash = sha256_stream(File::open(&path)?)?;
                result.insert(root.to_owned(), hash);
            }
        }
    }
    Ok(())
}

pub fn sha256_stream<R>(mut reader: R) -> Result<HashValue>
where
    R: Read,
{
    let mut context = Context::new(&SHA256);
    let mut buffer = [0; 1024];
    loop {
        let count = reader.read(&mut buffer)?;
        if count == 0 {
            break;
        }
        context.update(&buffer[..count]);
    }
    Ok(HashValue(context.finish().as_ref().to_vec()))
}