From 38e0f82812db460aeae55130ddbe360171554c31 Mon Sep 17 00:00:00 2001 From: Lucien Greathouse Date: Sat, 17 Nov 2018 00:04:44 -0800 Subject: [PATCH] Clean up VFS code to make it much more robust --- server/src/rbx_session.rs | 10 ++- server/src/session.rs | 22 ++++-- server/src/vfs.rs | 151 ++++++++++++++++++++++---------------- 3 files changed, 112 insertions(+), 71 deletions(-) diff --git a/server/src/rbx_session.rs b/server/src/rbx_session.rs index db4608bf..de3e16c1 100644 --- a/server/src/rbx_session.rs +++ b/server/src/rbx_session.rs @@ -37,8 +37,12 @@ impl RbxSession { } } - pub fn path_created_or_updated(&mut self, path: &Path) { - info!("Path changed: {}", path.display()); + pub fn path_created(&mut self, path: &Path) { + info!("Path created: {}", path.display()); + } + + pub fn path_updated(&mut self, path: &Path) { + info!("Path updated: {}", path.display()); } pub fn path_removed(&mut self, path: &Path) { @@ -121,7 +125,7 @@ fn construct_sync_point_node( ) { match vfs.get(&file_path) { Some(VfsItem::File(file)) => { - let contents = str::from_utf8(vfs.get_contents(&file.path).unwrap()).unwrap(); + let contents = str::from_utf8(&file.contents).unwrap(); let mut properties = HashMap::new(); properties.insert("Source".to_string(), RbxValue::String { value: contents.to_string() }); diff --git a/server/src/session.rs b/server/src/session.rs index 2cf03fed..a546c18b 100644 --- a/server/src/session.rs +++ b/server/src/session.rs @@ -90,21 +90,32 @@ impl Session { match watch_rx.recv() { Ok(event) => { match event { - DebouncedEvent::Create(path) | DebouncedEvent::Write(path) => { + DebouncedEvent::Create(path) => { { let mut vfs = vfs.lock().unwrap(); - vfs.add_or_update(&path).unwrap(); + vfs.path_created(&path).unwrap(); } { let mut rbx_session = rbx_session.lock().unwrap(); - rbx_session.path_created_or_updated(&path); + rbx_session.path_created(&path); + } + }, + DebouncedEvent::Write(path) => { + { + let mut vfs = vfs.lock().unwrap(); + vfs.path_updated(&path).unwrap(); + } + + { + let mut rbx_session = rbx_session.lock().unwrap(); + rbx_session.path_updated(&path); } }, DebouncedEvent::Remove(path) => { { let mut vfs = vfs.lock().unwrap(); - vfs.remove(&path); + vfs.path_removed(&path).unwrap(); } { @@ -115,8 +126,7 @@ impl Session { DebouncedEvent::Rename(from_path, to_path) => { { let mut vfs = vfs.lock().unwrap(); - vfs.remove(&from_path); - vfs.add_or_update(&to_path).unwrap(); + vfs.path_moved(&from_path, &to_path).unwrap(); } { diff --git a/server/src/vfs.rs b/server/src/vfs.rs index d02e8290..b744d567 100644 --- a/server/src/vfs.rs +++ b/server/src/vfs.rs @@ -7,7 +7,6 @@ use std::{ #[derive(Debug)] pub struct Vfs { - contents: HashMap>, items: HashMap, roots: HashSet, } @@ -15,81 +14,110 @@ pub struct Vfs { impl Vfs { pub fn new() -> Vfs { Vfs { - contents: HashMap::new(), items: HashMap::new(), roots: HashSet::new(), } } - pub fn add_root<'a, 'b>(&'a mut self, root_path: &'b Path) -> io::Result<&'a VfsItem> { - debug_assert!(root_path.is_absolute()); - - self.roots.insert(root_path.to_path_buf()); - - VfsItem::get(self, root_path) - } - pub fn get_roots(&self) -> &HashSet { &self.roots } pub fn get(&self, path: &Path) -> Option<&VfsItem> { debug_assert!(path.is_absolute()); - debug_assert!(self.is_valid_path(path)); + debug_assert!(self.is_within_roots(path)); self.items.get(path) } - pub fn get_contents(&self, path: &Path) -> Option<&[u8]> { + pub fn add_root(&mut self, path: &Path) -> io::Result<()> { debug_assert!(path.is_absolute()); - debug_assert!(self.is_valid_path(path)); + debug_assert!(!self.is_within_roots(path)); - self.contents.get(path).map(Vec::as_slice) + self.roots.insert(path.to_path_buf()); + + VfsItem::read_from_disk(self, path)?; + Ok(()) } - pub fn remove(&mut self, path: &Path) { + pub fn path_created(&mut self, path: &Path) -> io::Result<()> { debug_assert!(path.is_absolute()); - debug_assert!(self.is_valid_path(path)); + debug_assert!(self.is_within_roots(path)); - match self.items.remove(path) { - Some(item) => match item { - VfsItem::File(_) => { - self.contents.remove(path); - }, - VfsItem::Directory(VfsDirectory { children, .. }) => { - for child_path in &children { - self.remove(child_path); - } - }, - }, - None => {}, - } - } - - pub fn add_or_update<'a, 'b>(&'a mut self, path: &'b Path) -> io::Result<&'a VfsItem> { - debug_assert!(path.is_absolute()); - debug_assert!(self.is_valid_path(path)); - - VfsItem::get(self, path) - } - - fn is_valid_path(&self, path: &Path) -> bool { - let mut is_valid_path = false; - - for root_path in &self.roots { - if path.starts_with(root_path) { - is_valid_path = true; - break; + if let Some(parent_path) = path.parent() { + if self.is_within_roots(parent_path) && self.get(parent_path).is_none() { + self.path_created(parent_path)?; } } - is_valid_path + VfsItem::read_from_disk(self, path)?; + Ok(()) + } + + pub fn path_updated(&mut self, path: &Path) -> io::Result<()> { + debug_assert!(path.is_absolute()); + debug_assert!(self.is_within_roots(path)); + + if let Some(parent_path) = path.parent() { + if self.is_within_roots(parent_path) && self.get(parent_path).is_none() { + self.path_created(parent_path)?; + } + } + + VfsItem::read_from_disk(self, path)?; + Ok(()) + } + + pub fn path_removed(&mut self, path: &Path) -> io::Result<()> { + debug_assert!(path.is_absolute()); + debug_assert!(self.is_within_roots(path)); + + if let Some(parent_path) = path.parent() { + if self.is_within_roots(parent_path) { + if let Some(VfsItem::Directory(parent)) = self.items.get_mut(parent_path) { + parent.children.remove(path); + } + } + } + + match self.items.remove(path) { + Some(VfsItem::Directory(directory)) => { + for child_path in &directory.children { + self.path_removed(child_path)?; + } + }, + _ => {}, + } + + Ok(()) + } + + pub fn path_moved(&mut self, from_path: &Path, to_path: &Path) -> io::Result<()> { + debug_assert!(from_path.is_absolute()); + debug_assert!(self.is_within_roots(from_path)); + debug_assert!(to_path.is_absolute()); + debug_assert!(self.is_within_roots(to_path)); + + self.path_removed(from_path)?; + self.path_created(to_path)?; + Ok(()) + } + + fn is_within_roots(&self, path: &Path) -> bool { + for root_path in &self.roots { + if path.starts_with(root_path) { + return true; + } + } + + false } } #[derive(Debug)] pub struct VfsFile { pub path: PathBuf, + pub contents: Vec, } #[derive(Debug)] @@ -105,42 +133,41 @@ pub enum VfsItem { } impl VfsItem { - fn get<'a, 'b>(vfs: &'a mut Vfs, root_path: &'b Path) -> io::Result<&'a VfsItem> { - let metadata = fs::metadata(root_path)?; + fn read_from_disk<'a, 'b>(vfs: &'a mut Vfs, path: &'b Path) -> io::Result<&'a VfsItem> { + let metadata = fs::metadata(path)?; if metadata.is_file() { + let contents = fs::read(path)?; let item = VfsItem::File(VfsFile { - path: root_path.to_path_buf(), + path: path.to_path_buf(), + contents, }); - vfs.items.insert(root_path.to_path_buf(), item); + vfs.items.insert(path.to_path_buf(), item); - let contents = fs::read(root_path)?; - vfs.contents.insert(root_path.to_path_buf(), contents); - - Ok(vfs.items.get(root_path).unwrap()) + Ok(vfs.items.get(path).unwrap()) } else if metadata.is_dir() { let mut children = HashSet::new(); - for entry in fs::read_dir(root_path)? { + for entry in fs::read_dir(path)? { let entry = entry?; - let path = entry.path(); + let child_path = entry.path(); - VfsItem::get(vfs, &path)?; + VfsItem::read_from_disk(vfs, &child_path)?; - children.insert(path); + children.insert(child_path); } let item = VfsItem::Directory(VfsDirectory { - path: root_path.to_path_buf(), + path: path.to_path_buf(), children, }); - vfs.items.insert(root_path.to_path_buf(), item); + vfs.items.insert(path.to_path_buf(), item); - Ok(vfs.items.get(root_path).unwrap()) + Ok(vfs.items.get(path).unwrap()) } else { - unimplemented!(); + panic!("Unexpected non-file, non-directory item"); } } } \ No newline at end of file