1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
use std::collections::{HashMap, HashSet};
use std::path::{PathBuf, Path};
use crate::utils;
use crate::rif::rel::Relations;
use crate::models::FileStatus;
use crate::RifError;
/// Default level of checker node
const DEFAULT_LEVEL: i32 = 0;
/// Node that consits of checker's corelation tree
#[derive(Clone, Debug)]
struct Node {
path: PathBuf,
level: i32,
parent: Option<PathBuf>,
children: HashSet<PathBuf>,
}
impl Node {
fn new(path: &Path) -> Self {
Self {
path: path.to_owned(),
level: DEFAULT_LEVEL,
parent: None,
children: HashSet::new()
}
}
}
/// Checker that checks file's statuses
pub struct Checker {
node_map: HashMap<PathBuf, Node>,
existing: HashSet<PathBuf>,
non_existing: HashSet<PathBuf>,
}
impl Checker {
fn new() -> Self {
Self {
node_map: HashMap::new(),
existing: HashSet::new(),
non_existing: HashSet::new(),
}
}
/// Create checker with given rif list
///
/// # Args
/// * `rif_list` - Rif list to make node map from
pub fn with_relations(rif_list: &Relations) -> Result<Self, RifError> {
let mut checker = Checker::new();
for tuple in rif_list.files.iter() {
checker.add_node(tuple.0, &tuple.1.references)?;
// Clear is necessary because add_node utilizes internal cache for calculation
// This may be an optimal algorithm, yet it works.
checker.existing.clear();
checker.non_existing.clear();
}
Ok(checker)
}
/// Add node
///
/// This is an interanl method called by with_rif_list to add node to node map.
/// # Args
/// * `path` - File path of the node
/// * `children` - References of given given path
fn add_node(&mut self, path: &Path, children: &HashSet<PathBuf>) -> Result<(), RifError> {
// Update existing vector and non-existing vector
for child in children.iter() {
if self.node_map.contains_key(child) {
self.existing.insert(child.clone());
} else {
self.non_existing.insert(child.clone());
}
}
let highest_node_level = self.get_highest_node_level(&self.existing)?;
// Create new node and insert into node map
let mut target_node = Node::new(path);
target_node.level = highest_node_level + 1;
target_node.children = children.clone();
self.node_map.insert(path.to_owned(), target_node);
// If no reference node exits
// else, some reference node exists
if self.non_existing.len() == children.len() {
for child in children.iter() {
// Create child node and set necessary variables
let mut child_node = Node::new(child);
child_node.parent = Some(path.to_owned());
child_node.level = highest_node_level;
// Insert child node into hashmap
self.node_map.insert(child.clone(), child_node);
}
} else {
// Create non-existing nodes
for child in self.non_existing.iter() {
// Create child node and set necessary variables
let mut child_node = Node::new(child);
child_node.parent = Some(path.to_owned());
child_node.level = highest_node_level;
// Insert child node into hashmap
self.node_map.insert(child.clone(), child_node);
}
// Recursively increase a value by 1
self.recursive_increase(path)?;
} // if else end
Ok(())
} // function end
/// Check file references
///
/// This method check files' relation with references and set file' status according to referencing files' statues.
/// If referencing file is newer than a parent file or is stale, the parent becomes stale.
/// # Return value
/// This return vector of tuples (FileStatus, FilePath) which is used by hook trigger
///
/// # Args
/// * `rif_list` - Target rif list to check references
pub fn check(&mut self, rif_list: &mut Relations) -> Result<Vec<(FileStatus, PathBuf)>, RifError> {
// 1. Sort lists
// 2. and compare children's references
// 3. Also check filestamp
let sorted = self.get_sorted_vec();
let mut changed_files: Vec<(FileStatus, PathBuf)> = Vec::new();
for target_key in sorted.iter() {
// New file status that will be set to the 'item'
// Default status is fresh so that file is automatically fresh
// when there are no references.
let mut status = FileStatus::Fresh;
// Item is a node retrieved with item_key
if let Some(target_node) = self.node_map.get(target_key) {
// item_ref_keys are vector of keys which parent is the 'item'
let target_ref_keys = &self.node_map.get(&target_node.path).unwrap().children;
for key in target_ref_keys.iter() {
// Child single_File that is the child of node 'item'
if let Some(child_file) = rif_list.files.get(key) {
// Made status public for debugging
// If child is stale, then parent is automatically stale
if let FileStatus::Stale = child_file.status {
status = FileStatus::Stale;
break;
}
// If child is fresh but fresher than parent, then parent is stale
if child_file.timestamp > rif_list.files.get(target_key).unwrap().timestamp {
status = FileStatus::Stale;
break;
}
}
}
} else {
// No node found from item_key
return Err(RifError::CheckerError(String::from("Failed to find item from key")));
}
// Set new status into rif_list
if let Some(file) = rif_list.files.get_mut(target_key) {
// Print status changes into stdout
if file.status != status {
println!("Status update \"{}\" {}", utils::green(&target_key.display().to_string()), status);
// Add file to changed files
changed_files.push((status, target_key.to_path_buf()));
}
file.status = status;
} else {
return Err(RifError::CheckerError(String::from("Failed to find item from rif list")));
}
} // for loop end
Ok(changed_files)
}
/// Get sorted keys by level from node map
///
/// Sorted vector starts with a node that has the lowest level which enables checker method to safely assume that file check doesn't overlook file modifications.
fn get_sorted_vec(&self) -> Vec<PathBuf> {
let mut return_vec = vec![];
let mut node_vec: Vec<(PathBuf, Node)>
= self.node_map.clone().into_iter().collect();
// Sort nodes by levels
node_vec.sort_by(|a, b| b.1.level.cmp(&a.1.level));
// get only keys from node vector
for tuple in node_vec { return_vec.push(tuple.0); }
return_vec
}
/// Get node which has highest level from given node set
///
/// Highest level is used to set newly created node's level.
/// # Args
/// * `children` - Hahset of node keys that used for comparisons.
fn get_highest_node_level(&self, children : &HashSet<PathBuf>) -> Result<i32, RifError> {
let children: Vec<&Path> = children.iter().map(|p| p.as_path()).collect();
// Early return if children's lenth is 0
if children.len() == 0 {
return Ok(DEFAULT_LEVEL);
}
// Set first children's level as a highest level for now.
let mut highest =
if let Some(value) = self.node_map.get(children[0]) {
value.level
} else {
return Err(RifError::CheckerError(format!("Failed to get highest number from given children\n{:#?}", children)));
};
// Iterate through children and update a highest level
// If higher number is found.
for index in 1..children.len() {
if let Some(value) = self.node_map.get(children[index]) {
if highest < value.level {
highest = value.level;
}
} else {
return Err(RifError::CheckerError(format!("Failed to get highest number from given children\nFrom tree:\n{:#?}\nItem:\n{}", children, children[index].display())));
};
}
Ok(highest)
} // function end
/// Recursively increase node by following upward starting from given path
///
/// Used when some children node's were newly created to guarantee that children's level is always lower than that of parent's.
fn recursive_increase(&mut self, path: &Path) -> Result<(), RifError> {
// Recursively increase the level from path to top level
// Base case
self.node_map.get_mut(path).unwrap().level += 1;
// Current node position
let mut target_path = path.to_owned();
loop {
// Get parent if possible
// else, there is no parent, break from loop
if let Some(parent_path) = self.node_map.get(&target_path).unwrap().parent.clone() {
self.node_map.get_mut(&parent_path).unwrap().level += 1;
target_path = parent_path;
} else {
break;
}
}
Ok(())
} // function end
}