checksums/ops/
compare.rs

1use self::super::super::util::{vec_merge, mul_str};
2use std::collections::BTreeMap;
3
4
5#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
6pub enum CompareResult {
7    FileAdded(String),
8    FileRemoved(String),
9    FileIgnored(String),
10}
11
12#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
13pub enum CompareFileResult {
14    FileMatches(String),
15    FileDiffers {
16        file: String,
17        was_hash: String,
18        new_hash: String,
19    },
20}
21
22#[derive(Debug, Clone, Hash, PartialEq, Eq)]
23pub enum CompareError {
24    HashLengthDiffers {
25        previous_len: usize,
26        current_len: usize,
27    },
28}
29
30
31/// Compare two provided hashes.
32pub fn compare_hashes(out_file: &str, mut current_hashes: BTreeMap<String, String>, mut loaded_hashes: BTreeMap<String, String>)
33                      -> Result<(Vec<CompareResult>, Vec<CompareFileResult>), CompareError> {
34    let current_hashes_value_len = current_hashes.iter().next().unwrap().1.len();
35    let loaded_hashes_value_len = loaded_hashes.iter().next().unwrap().1.len();
36    if current_hashes_value_len != loaded_hashes_value_len {
37        return Err(CompareError::HashLengthDiffers {
38            previous_len: loaded_hashes_value_len,
39            current_len: current_hashes_value_len,
40        });
41    }
42    let placeholder_value = mul_str("-", current_hashes_value_len);
43    let mut file_compare_results = Vec::new();
44
45    current_hashes.remove(out_file);
46    loaded_hashes.remove(out_file);
47
48    let remove_results = process_ignores(|key, _, other| !other.contains_key(key),
49                                         CompareResult::FileAdded,
50                                         CompareResult::FileRemoved,
51                                         &mut current_hashes,
52                                         &mut loaded_hashes);
53    let ignore_results = process_ignores(|_, value, _| *value == placeholder_value,
54                                         CompareResult::FileIgnored,
55                                         CompareResult::FileIgnored,
56                                         &mut current_hashes,
57                                         &mut loaded_hashes);
58
59
60    // By this point both hashes have the same keysets
61    assert_eq!(current_hashes.len(), loaded_hashes.len());
62
63    if !current_hashes.is_empty() {
64        for (key, loaded_value) in loaded_hashes {
65            let current_value = &current_hashes[&key];
66            if *current_value == loaded_value {
67                file_compare_results.push(CompareFileResult::FileMatches(key));
68            } else {
69                file_compare_results.push(CompareFileResult::FileDiffers {
70                    file: key,
71                    was_hash: loaded_value,
72                    new_hash: current_value.clone(),
73                });
74            }
75        }
76    }
77
78    Ok((vec_merge(remove_results, ignore_results), file_compare_results))
79}
80
81
82fn process_ignores<F, Rc, Rl>(f: F, cres: Rc, lres: Rl, ch: &mut BTreeMap<String, String>, lh: &mut BTreeMap<String, String>) -> Vec<CompareResult>
83    where F: Fn(&str, &str, &BTreeMap<String, String>) -> bool,
84          Rc: Fn(String) -> CompareResult,
85          Rl: Fn(String) -> CompareResult
86{
87    let mut results = Vec::new();
88    let mut keys_to_remove = Vec::new();
89
90    process_ignores_iter(&f, &cres, ch, lh, &mut keys_to_remove, &mut results);
91    process_ignores_iter(&f, &lres, lh, ch, &mut keys_to_remove, &mut results);
92
93    for key in keys_to_remove {
94        ch.remove(&key);
95        lh.remove(&key);
96    }
97
98    results
99}
100
101fn process_ignores_iter<F, R>(f: &F, res: &R, curr: &BTreeMap<String, String>, other: &BTreeMap<String, String>, keys_to_remove: &mut Vec<String>,
102                              results: &mut Vec<CompareResult>)
103    where F: Fn(&str, &str, &BTreeMap<String, String>) -> bool,
104          R: Fn(String) -> CompareResult
105{
106    for (key, value) in curr {
107        if f(key, value, other) {
108            results.push(res(key.clone()));
109            keys_to_remove.push(key.clone());
110        }
111    }
112}