Major Subsystem Rewrite (Reconciler Mk5) (#217)

This commit is contained in:
Lucien Greathouse 2019-08-27 15:00:37 -07:00 • committed by GitHub
parent 8e8291a0bd
commit fea303ac8b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
80 changed files with 3843 additions and 5609 deletions

View file

@ -18,6 +18,14 @@ struct PathMapNode<T> {
#[derive(Debug, Serialize)]
pub struct PathMap<T> {
nodes: HashMap<PathBuf, PathMapNode<T>>,
/// Contains the set of all paths whose parent either does not exist, or is
/// not present in the PathMap.
///
/// Note that these paths may have other _ancestors_ in the tree, but if an
/// orphan's parent path is ever inserted, it will stop being an orphan. It
/// will be... adopted!
orphan_paths: HashSet<PathBuf>,
}
impl<T> Default for PathMap<T> {
@ -30,49 +38,71 @@ impl<T> PathMap<T> {
pub fn new() -> PathMap<T> {
PathMap {
nodes: HashMap::new(),
orphan_paths: HashSet::new(),
}
}
pub fn get(&self, path: &Path) -> Option<&T> {
self.nodes.get(path).map(|v| &v.value)
pub fn get(&self, path: impl AsRef<Path>) -> Option<&T> {
self.nodes.get(path.as_ref()).map(|v| &v.value)
}
pub fn get_mut(&mut self, path: &Path) -> Option<&mut T> {
self.nodes.get_mut(path).map(|v| &mut v.value)
pub fn get_mut(&mut self, path: impl AsRef<Path>) -> Option<&mut T> {
self.nodes.get_mut(path.as_ref()).map(|v| &mut v.value)
}
pub fn insert(&mut self, path: PathBuf, value: T) {
if let Some(parent_path) = path.parent() {
if let Some(parent) = self.nodes.get_mut(parent_path) {
parent.children.insert(path.to_path_buf());
pub fn children(&self, path: impl AsRef<Path>) -> Option<Vec<&Path>> {
self.nodes.get(path.as_ref()).map(|v| v.children.iter().map(AsRef::as_ref).collect())
}
pub fn contains_key(&self, path: impl AsRef<Path>) -> bool {
self.nodes.contains_key(path.as_ref())
}
pub fn insert(&mut self, path: impl Into<PathBuf>, value: T) {
let path = path.into();
self.add_to_parent(path.clone());
// Collect any children that are currently marked as orphaned paths, but
// are actually children of this new node.
let mut children = HashSet::new();
for orphan_path in &self.orphan_paths {
if orphan_path.parent() == Some(&path) {
children.insert(orphan_path.clone());
}
}
for child in &children {
self.orphan_paths.remove(child);
}
self.nodes.insert(path, PathMapNode {
value,
children: HashSet::new(),
children,
});
}
pub fn remove(&mut self, root_path: &Path) -> Option<T> {
if let Some(parent_path) = root_path.parent() {
if let Some(parent) = self.nodes.get_mut(parent_path) {
parent.children.remove(root_path);
}
}
/// Remove the given path and all of its linked descendants, returning all
/// values stored in the map.
pub fn remove(&mut self, root_path: impl AsRef<Path>) -> Vec<(PathBuf, T)> {
let root_path = root_path.as_ref();
let mut root_node = match self.nodes.remove(root_path) {
self.remove_from_parent(root_path);
let (root_path, root_node) = match self.nodes.remove_entry(root_path) {
Some(node) => node,
None => return None,
None => return Vec::new(),
};
let root_value = root_node.value;
let mut to_visit: Vec<PathBuf> = root_node.children.drain().collect();
let mut removed_entries = vec![(root_path, root_node.value)];
let mut to_visit: Vec<PathBuf> = root_node.children.into_iter().collect();
while let Some(path) = to_visit.pop() {
match self.nodes.remove(&path) {
Some(mut node) => {
for child in node.children.drain() {
match self.nodes.remove_entry(&path) {
Some((path, node)) => {
removed_entries.push((path, node.value));
for child in node.children.into_iter() {
to_visit.push(child);
}
},
@ -82,7 +112,7 @@ impl<T> PathMap<T> {
}
}
Some(root_value)
removed_entries
}
/// Traverses the route between `start_path` and `target_path` and returns
@ -93,10 +123,13 @@ impl<T> PathMap<T> {
/// FS events, a file remove event could be followed by that file's
/// directory being removed, in which case we should process that
/// directory's parent.
pub fn descend(&self, start_path: &Path, target_path: &Path) -> PathBuf {
let relative_path = target_path.strip_prefix(start_path)
pub fn descend(&self, start_path: impl Into<PathBuf>, target_path: impl AsRef<Path>) -> PathBuf {
let start_path = start_path.into();
let target_path = target_path.as_ref();
let relative_path = target_path.strip_prefix(&start_path)
.expect("target_path did not begin with start_path");
let mut current_path = start_path.to_path_buf();
let mut current_path = start_path;
for component in relative_path.components() {
match component {
@ -115,4 +148,131 @@ impl<T> PathMap<T> {
current_path
}
/// Adds the path to its parent if it's present in the tree, or the set of
/// orphaned paths if it is not.
fn add_to_parent(&mut self, path: PathBuf) {
if let Some(parent_path) = path.parent() {
if let Some(parent) = self.nodes.get_mut(parent_path) {
parent.children.insert(path);
return;
}
}
// In this branch, the path is orphaned because it either doesn't have a
// parent according to Path, or because its parent doesn't exist in the
// PathMap.
self.orphan_paths.insert(path);
}
/// Removes the path from its parent, or from the orphaned paths set if it
/// has no parent.
fn remove_from_parent(&mut self, path: &Path) {
if let Some(parent_path) = path.parent() {
if let Some(parent) = self.nodes.get_mut(parent_path) {
parent.children.remove(path);
return;
}
}
// In this branch, the path is orphaned because it either doesn't have a
// parent according to Path, or because its parent doesn't exist in the
// PathMap.
self.orphan_paths.remove(path);
}
}
#[cfg(test)]
mod test {
use super::*;
use maplit::hashset;
#[test]
fn smoke_test() {
let mut map = PathMap::new();
assert_eq!(map.get("/foo"), None);
map.insert("/foo", 5);
assert_eq!(map.get("/foo"), Some(&5));
map.insert("/foo/bar", 6);
assert_eq!(map.get("/foo"), Some(&5));
assert_eq!(map.get("/foo/bar"), Some(&6));
assert_eq!(map.children("/foo"), Some(vec![Path::new("/foo/bar")]));
}
#[test]
fn orphans() {
let mut map = PathMap::new();
map.insert("/foo/bar", 5);
assert_eq!(map.orphan_paths, hashset!["/foo/bar".into()]);
map.insert("/foo", 6);
assert_eq!(map.orphan_paths, hashset!["/foo".into()]);
}
#[test]
fn remove_one() {
let mut map = PathMap::new();
map.insert("/foo", 6);
assert_eq!(map.remove("/foo"), vec![
(PathBuf::from("/foo"), 6),
]);
assert_eq!(map.get("/foo"), None);
}
#[test]
fn remove_child() {
let mut map = PathMap::new();
map.insert("/foo", 6);
map.insert("/foo/bar", 12);
assert_eq!(map.remove("/foo"), vec![
(PathBuf::from("/foo"), 6),
(PathBuf::from("/foo/bar"), 12),
]);
assert_eq!(map.get("/foo"), None);
assert_eq!(map.get("/foo/bar"), None);
}
#[test]
fn remove_descendant() {
let mut map = PathMap::new();
map.insert("/foo", 6);
map.insert("/foo/bar", 12);
map.insert("/foo/bar/baz", 18);
assert_eq!(map.remove("/foo"), vec![
(PathBuf::from("/foo"), 6),
(PathBuf::from("/foo/bar"), 12),
(PathBuf::from("/foo/bar/baz"), 18),
]);
assert_eq!(map.get("/foo"), None);
assert_eq!(map.get("/foo/bar"), None);
assert_eq!(map.get("/foo/bar/baz"), None);
}
#[test]
fn remove_not_orphan_descendants() {
let mut map = PathMap::new();
map.insert("/foo", 6);
map.insert("/foo/bar/baz", 12);
assert_eq!(map.remove("/foo"), vec![
(PathBuf::from("/foo"), 6),
]);
assert_eq!(map.get("/foo"), None);
assert_eq!(map.get("/foo/bar/baz"), Some(&12));
}
}