use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::mpsc::Receiver;
use std::rc::Rc;
use types::{Change, HistoryNode, HistoryTree, Link, PathSet};
use parsing::ParsedCommit;
struct HistoryState<'a, T, V, F>
where V: Fn(&ParsedCommit) -> T, F: Fn(&ParsedCommit) -> bool {
history: HistoryTree<T>,
pending_edges: HashMap<String, Vec<Link<HistoryNode<T>>>>,
path_set: &'a PathSet,
visitor: V,
filter: F,
}
impl<'a, T, V, F> HistoryState<'a, T, V, F>
where V: Fn(&ParsedCommit) -> T, F: Fn(&ParsedCommit) -> bool {
fn new(set: &'a PathSet, vis: V, fil: F) -> HistoryState<T, V, F> {
let mut pending = HashMap::new();
for path in set {
pending.insert(path.clone(), Vec::new());
}
HistoryState{ history: HistoryTree::new(),
pending_edges: pending,
path_set: set,
visitor: vis,
filter: fil
}
}
fn new_node(&self, d: Option<Rc<T>>) -> Link<HistoryNode<T>> {
Rc::new(RefCell::new(HistoryNode{data: d, previous: None}))
}
fn append_commit(&mut self, commit: &ParsedCommit) {
let data = if (self.filter)(commit) {
Some(Rc::new((self.visitor)(commit)))
}
else {
None
};
for delta in &commit.deltas {
if !self.pending_edges.contains_key(&delta.path) {
continue;
}
let new_node = self.new_node(data.clone());
self.append_node(&delta.path, new_node.clone());
match delta.change {
Change::Modified => {
self.pending_edges.entry(delta.path.clone())
.or_insert_with(Vec::new)
.push(new_node);
}
Change::Added |
Change::Deleted => { }
Change::Copied{..} | Change::Renamed{..} => {
self.pending_edges.entry(delta.from.clone())
.or_insert_with(Vec::new)
.push(new_node);
}
}
}
}
fn append_node(&mut self, key: &str, node: Link<HistoryNode<T>>) {
self.build_edges(key, &node);
if !self.history.contains_key(key) && self.path_set.contains(key) {
self.history.insert(key.to_string(), node);
}
}
fn build_edges(&mut self, for_path: &str, link_to: &Link<HistoryNode<T>>) {
let from_set = match self.pending_edges.remove(for_path) {
None => return, Some(to_link) => to_link
};
for l in from_set { assert!(l.borrow().previous.is_none());
l.borrow_mut().previous = Some(link_to.clone());
}
}
}
pub fn gather_history<T, V, F>(paths: &PathSet, v: V, f: F,
commit_source: &Receiver<ParsedCommit>) -> HistoryTree<T>
where V: Fn(&ParsedCommit) -> T, F: Fn(&ParsedCommit) -> bool {
let mut state = HistoryState::new(paths, v, f);
while let Ok(commit) = commit_source.recv() {
state.append_commit(&commit);
}
state.history
}