rmd 0.5.3

An improved rm implementation able to remove duplicate files
Documentation
extern crate data_encoding;
extern crate ring;

use std::collections::HashSet;
use std::fs::File;
use std::io::prelude::*;
use std::io::Error;
use std::path::Path;

use data_encoding::HEXUPPER;
use ring::digest::{Context, SHA256};

use crate::file_remove_iterator::file_remove::FileRemove;

pub struct FileIndex {
    store: HashSet<String>,
}

impl FileIndex {
    pub fn new() -> FileIndex {
        FileIndex {
            store: HashSet::new(),
        }
    }
}

impl FileRemove for FileIndex {
    fn remove(&mut self, path: &Path) -> Result<bool, Error> {
        let hash = hash_file(path)?;
        if self.store.contains(&hash) {
            return Ok(true);
        }
        self.store.insert(hash);
        Ok(false)
    }
}

fn hash_file(path: &Path) -> Result<String, Error> {
    let mut input = File::open(path)?;
    let mut buff = [0; 1024];
    let mut context = Context::new(&SHA256);

    loop {
        let count = input.read(&mut buff)?;
        if count == 0 {
            break;
        }
        context.update(&buff[..count]);
    }

    let digest = context.finish();
    let hash = HEXUPPER.encode(digest.as_ref());
    Ok(hash)
}

#[cfg(test)]
mod test {

    extern crate tempfile;

    use super::*;
    use std::io::prelude::Write;
    use tempfile::tempdir;

    #[test]
    fn test_file_hash() {
        let dir = tempdir().unwrap();
        let msg = "A RANDOM MESSAGE\n";
        let path = dir.path().to_owned();
        let path = path.join("RANDOM_NAME");
        let mut file = File::create(&path).unwrap();
        file.write(msg.as_bytes()).unwrap();

        let ans = "2FF711FDB1CB48EA4B1BBD34C5CE5817921AC0FC852B34DAEB250D1293DE8B63";
        assert_eq!(ans, hash_file(&path).unwrap());
    }

    #[test]
    fn test_file_index() {
        let names = vec!["unique", "equal_1", "equal_2"];
        let dir = tempdir().unwrap();

        let eq_data = "EQUAL FILES";
        let uniq_data = "UNIQUE FILE";

        let base_dir = dir.path().to_owned();

        let file_path = base_dir.join(names[0]);
        let mut unique_file = File::create(&file_path).unwrap();
        unique_file.write(uniq_data.as_bytes()).unwrap();

        for name in names.iter().skip(1) {
            let mut eq_file = File::create(dir.path().join(name)).unwrap();
            eq_file.write(eq_data.as_bytes()).unwrap();
        }

        let ans = vec![true, true, false];

        let mut index = FileIndex::new();
        for (n, c) in names.iter().zip(ans.iter()) {
            let a = !index.remove(dir.path().join(n).as_path()).unwrap();
            assert_eq!(a, *c);
        }
    }
}