Documentation
use std::fs::read_dir;
use std::path::Path;
use crypto::digest::Digest;
use crypto::md5::Md5;

pub fn md5(path: &Path) -> Result<String, std::io::Error> {
    let mut m = Md5::new();
    m.input(std::fs::read(path)?.as_slice());
    Ok(m.result_str())
}

pub fn read_dir_(path: &Path, f: impl Fn(&Path) + Copy) -> Result<(), std::io::Error> {
    let dir = read_dir(path)?;
    for re in dir {
        let file = re?;
        if let Ok(filetype) = file.file_type() {
            if filetype.is_dir() {
                read_dir_(&file.path(), f)?;
            } else {
                f(&file.path());
            } 
        }
    }
    Ok(())
}

#[cfg(test)]
#[test]
fn test_video_check() {
    use std::cell::RefCell;
    use std::collections::HashMap;
    use crypto::digest::Digest;
    use crypto::md5::Md5;
    use std::io;
    use std::cmp::max;
    use std::thread;
    use std::sync::{Arc, Mutex};

    let path = std::path::Path::new("/mnt/视频");
    let files = RefCell::new(HashMap::<u64, Vec<String>>::new());
    read_dir_(&path, |p| {
        let m = if let Ok(metadata) = p.metadata() {
            metadata
        } else {
            return;
        };
        let mut file_map = files.borrow_mut();
        if !file_map.contains_key(&m.len()) {
            file_map.insert(m.len(), Vec::new());
            file_map.get_mut(&m.len()).unwrap()
                .push(String::from(p.to_str().unwrap()));
        } else {
            file_map.get_mut(&m.len()).unwrap()
                .push(String::from(p.to_str().unwrap()));
        }
    }).unwrap();

    let get_num = |info| -> usize {
        println!("{}", info);
        let mut s = String::new();
        let stdin = io::stdin();
        stdin.read_line(&mut s).unwrap();
        s.pop();
        s.parse().unwrap()
    };

    let mut delete_size: u64 = 0;
    for (k, v) in files.borrow().iter() {
        if v.len() > 1 {
            let size = k.clone() as f64 / 1024i32.pow(2) as f64;
            println!("len {:.02}MB: {:#?}", size, v);
            let md5s = Arc::new(Mutex::new(Vec::<String>::new()));
            let mut ths = vec![];
            for i in 0..v.len() {
                /**/let md5_list = Arc::clone(&md5s);
                let v_temp = v.clone();
                ths.push(thread::spawn(move || {
                    let mut md5 = Md5::new();
                    md5.input(std::fs::read(Path::new(&v_temp[i])).unwrap().as_slice());
                    md5_list.lock().unwrap()
                        .push(md5.result_str());
                    println!("{}: Md5 {{{}}}", &v_temp[i], md5.result_str());
                }));
            }
            for th in ths {
                th.join().unwrap();
            }
            let mut md5s_temp = Arc::clone(&md5s).lock().unwrap().clone();
            md5s_temp.dedup();
            if md5s_temp.len() != 1 {
                println!("EXISTS OUT OF ONE {:?} {{{:#?}}}", md5s_temp, v);
            } else {
                let mut templen = v[0].len();
                let mut at: usize = 0;
                let mut max_len = v[0].len();
                for i in 1..v.len() {
                    if &v[i].len() < &templen {
                        templen = v[i].len();
                        at = i;
                    }
                    max_len = max(max_len, templen);
                }
                if max_len - v[at].len() > 1 {
                    for i in 0..v.len() {
                        println!("{}: {}", i, &v[i]);
                    }
                    let n = get_num("ENSURE SAVE: ");
                    if n < v.len() {
                        println!("SAVE '{}'", &v[n]);
                        at = n;
                    } else {
                        continue;
                    }
                } else {
                    println!("SAVE '{}'", &v[at]);
                }
                for i in 0..v.len() {
                    if i != at {
                        println!("DELETE '{}'", &v[i]);
                        std::fs::remove_file(Path::new(&v[i])).unwrap();
                        delete_size += k.clone();
                    }
                }
            }
            println!("\ndelete_size: {:.02}MB\n\n", delete_size as f64 / 1024i32.pow(2) as f64);
        }
    }
}