vfs: Flesh out MemoryBackend

This commit is contained in:
Lucien Greathouse
2020-02-21 23:52:11 -08:00
parent fefc7a69cd
commit bb2dcbaea0
2 changed files with 109 additions and 50 deletions

View File

@@ -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<dyn Iterator<Item = io::Result<DirEntry>>>,
pub(crate) inner: Box<dyn Iterator<Item = io::Result<DirEntry>>>,
}
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 {

View File

@@ -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<P: Into<PathBuf>>(&mut self, path: P, snapshot: VfsSnapshot) {
pub fn load_snapshot<P: Into<PathBuf>>(
&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<Vec<u8>> {
Err(io::Error::new(
io::ErrorKind::Other,
"MemoryBackend doesn't do anything",
))
fn read(&mut self, path: &Path) -> io::Result<Vec<u8>> {
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<ReadDir> {
Err(io::Error::new(
io::ErrorKind::Other,
"MemoryBackend doesn't do anything",
))
fn read_dir(&mut self, path: &Path) -> io::Result<ReadDir> {
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<Metadata> {
Err(io::Error::new(
io::ErrorKind::Other,
"MemoryBackend doesn't do anything",
))
fn metadata(&mut self, path: &Path) -> io::Result<Metadata> {
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<VfsEvent> {
@@ -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<T>(path: &Path) -> io::Result<T> {
Err(io::Error::new(
io::ErrorKind::Other,
format!(
"path {} was a directory, but must be a file",
path.display()
),
))
}
fn must_be_dir<T>(path: &Path) -> io::Result<T> {
Err(io::Error::new(
io::ErrorKind::Other,
format!(
"path {} was a file, but must be a directory",
path.display()
),
))
}
fn not_found<T>(path: &Path) -> io::Result<T> {
Err(io::Error::new(
io::ErrorKind::NotFound,
format!("path {} not found", path.display()),
))
}