From bb2dcbaea0745b93f838870465abb1db7db4c4bb Mon Sep 17 00:00:00 2001 From: Lucien Greathouse Date: Fri, 21 Feb 2020 23:52:11 -0800 Subject: [PATCH] vfs: Flesh out MemoryBackend --- vfs/src/lib.rs | 6 +- vfs/src/memory_backend.rs | 153 ++++++++++++++++++++++++++------------ 2 files changed, 109 insertions(+), 50 deletions(-) diff --git a/vfs/src/lib.rs b/vfs/src/lib.rs index 238c6d81..6105a033 100644 --- a/vfs/src/lib.rs +++ b/vfs/src/lib.rs @@ -65,7 +65,7 @@ pub trait VfsBackend: sealed::Sealed + Send + 'static { /// /// [std::fs::DirEntry]: https://doc.rust-lang.org/stable/std/fs/struct.DirEntry.html pub struct DirEntry { - path: PathBuf, + pub(crate) path: PathBuf, } impl DirEntry { @@ -78,7 +78,7 @@ impl DirEntry { /// /// [std::fs::ReadDir]: https://doc.rust-lang.org/stable/std/fs/struct.ReadDir.html pub struct ReadDir { - inner: Box>>, + pub(crate) inner: Box>>, } impl Iterator for ReadDir { @@ -94,7 +94,7 @@ impl Iterator for ReadDir { /// [std::fs::Metadata]: https://doc.rust-lang.org/stable/std/fs/struct.Metadata.html #[derive(Debug)] pub struct Metadata { - is_file: bool, + pub(crate) is_file: bool, } impl Metadata { diff --git a/vfs/src/memory_backend.rs b/vfs/src/memory_backend.rs index f1b66788..b7c8ac13 100644 --- a/vfs/src/memory_backend.rs +++ b/vfs/src/memory_backend.rs @@ -1,8 +1,8 @@ -use std::collections::{BTreeSet, HashMap}; +use std::collections::{BTreeSet, HashMap, VecDeque}; use std::io; use std::path::{Path, PathBuf}; -use crate::{Metadata, ReadDir, VfsBackend, VfsEvent, VfsSnapshot}; +use crate::{DirEntry, Metadata, ReadDir, VfsBackend, VfsEvent, VfsSnapshot}; /// `VfsBackend` that reads from an in-memory filesystem, intended for setting /// up testing scenarios quickly. @@ -20,7 +20,11 @@ impl MemoryBackend { } } - pub fn load_snapshot>(&mut self, path: P, snapshot: VfsSnapshot) { + pub fn load_snapshot>( + &mut self, + path: P, + snapshot: VfsSnapshot, + ) -> io::Result<()> { let path = path.into(); if let Some(parent_path) = path.parent() { @@ -28,10 +32,7 @@ impl MemoryBackend { if let Entry::Dir { children } = parent_entry { children.insert(path.clone()); } else { - panic!( - "Tried to load snapshot as child of file, {}", - parent_path.display() - ); + return must_be_dir(parent_path); } } else { self.orphans.insert(path.clone()); @@ -54,10 +55,25 @@ impl MemoryBackend { for (child_name, child) in children { let full_path = path.join(child_name); - self.load_snapshot(full_path, child); + self.load_snapshot(full_path, child)?; } } - }; + } + + Ok(()) + } + + fn remove(&mut self, root_path: PathBuf) { + self.orphans.remove(&root_path); + + let mut to_remove = VecDeque::new(); + to_remove.push_back(root_path); + + while let Some(path) = to_remove.pop_front() { + if let Some(Entry::Dir { children }) = self.entries.remove(&path) { + to_remove.extend(children); + } + } } } @@ -69,46 +85,68 @@ enum Entry { } impl VfsBackend for MemoryBackend { - fn read(&mut self, _path: &Path) -> io::Result> { - Err(io::Error::new( - io::ErrorKind::Other, - "MemoryBackend doesn't do anything", - )) + fn read(&mut self, path: &Path) -> io::Result> { + match self.entries.get(path) { + Some(Entry::File { contents }) => Ok(contents.clone()), + Some(Entry::Dir { .. }) => must_be_file(path), + None => not_found(path), + } } - fn write(&mut self, _path: &Path, _data: &[u8]) -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Other, - "MemoryBackend doesn't do anything", - )) + fn write(&mut self, path: &Path, data: &[u8]) -> io::Result<()> { + self.load_snapshot( + path, + VfsSnapshot::File { + contents: data.to_owned(), + }, + ) } - fn read_dir(&mut self, _path: &Path) -> io::Result { - Err(io::Error::new( - io::ErrorKind::Other, - "MemoryBackend doesn't do anything", - )) + fn read_dir(&mut self, path: &Path) -> io::Result { + match self.entries.get(path) { + Some(Entry::Dir { children }) => { + let iter = children + .clone() + .into_iter() + .map(|path| Ok(DirEntry { path })); + + Ok(ReadDir { + inner: Box::new(iter), + }) + } + Some(Entry::File { .. }) => must_be_dir(path), + None => not_found(path), + } } - fn remove_file(&mut self, _path: &Path) -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Other, - "MemoryBackend doesn't do anything", - )) + fn remove_file(&mut self, path: &Path) -> io::Result<()> { + match self.entries.get(path) { + Some(Entry::File { .. }) => { + self.remove(path.to_owned()); + Ok(()) + } + Some(Entry::Dir { .. }) => must_be_file(path), + None => not_found(path), + } } - fn remove_dir_all(&mut self, _path: &Path) -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Other, - "MemoryBackend doesn't do anything", - )) + fn remove_dir_all(&mut self, path: &Path) -> io::Result<()> { + match self.entries.get(path) { + Some(Entry::Dir { .. }) => { + self.remove(path.to_owned()); + Ok(()) + } + Some(Entry::File { .. }) => must_be_dir(path), + None => not_found(path), + } } - fn metadata(&mut self, _path: &Path) -> io::Result { - Err(io::Error::new( - io::ErrorKind::Other, - "MemoryBackend doesn't do anything", - )) + fn metadata(&mut self, path: &Path) -> io::Result { + match self.entries.get(path) { + Some(Entry::File { .. }) => Ok(Metadata { is_file: true }), + Some(Entry::Dir { .. }) => Ok(Metadata { is_file: false }), + None => not_found(path), + } } fn event_receiver(&self) -> crossbeam_channel::Receiver { @@ -116,16 +154,37 @@ impl VfsBackend for MemoryBackend { } fn watch(&mut self, _path: &Path) -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Other, - "MemoryBackend doesn't do anything", - )) + Ok(()) } fn unwatch(&mut self, _path: &Path) -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Other, - "MemoryBackend doesn't do anything", - )) + Ok(()) } } + +fn must_be_file(path: &Path) -> io::Result { + Err(io::Error::new( + io::ErrorKind::Other, + format!( + "path {} was a directory, but must be a file", + path.display() + ), + )) +} + +fn must_be_dir(path: &Path) -> io::Result { + Err(io::Error::new( + io::ErrorKind::Other, + format!( + "path {} was a file, but must be a directory", + path.display() + ), + )) +} + +fn not_found(path: &Path) -> io::Result { + Err(io::Error::new( + io::ErrorKind::NotFound, + format!("path {} not found", path.display()), + )) +}