From fefc7a69cd0c5e08d67793d8c815848009a83f47 Mon Sep 17 00:00:00 2001 From: Lucien Greathouse Date: Fri, 21 Feb 2020 23:34:56 -0800 Subject: [PATCH] vfs: Expand documentation --- vfs/src/lib.rs | 150 +++++++++++++++++++++++++++++++++----- vfs/src/memory_backend.rs | 4 +- 2 files changed, 135 insertions(+), 19 deletions(-) diff --git a/vfs/src/lib.rs b/vfs/src/lib.rs index 8a59b7e0..238c6d81 100644 --- a/vfs/src/lib.rs +++ b/vfs/src/lib.rs @@ -5,7 +5,7 @@ mod std_backend; use std::io; use std::path::{Path, PathBuf}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, MutexGuard}; pub use memory_backend::MemoryBackend; pub use noop_backend::NoopBackend; @@ -15,6 +15,7 @@ pub use std_backend::StdBackend; mod sealed { use super::*; + /// Sealing trait for VfsBackend. pub trait Sealed {} impl Sealed for MemoryBackend {} @@ -22,6 +23,9 @@ mod sealed { impl Sealed for StdBackend {} } +/// Trait that transforms `io::Result` into `io::Result>`. +/// +/// `Ok(None)` takes the place of IO errors whose `io::ErrorKind` is `NotFound`. pub trait IoResultExt { fn with_not_found(self) -> io::Result>; } @@ -57,6 +61,9 @@ pub trait VfsBackend: sealed::Sealed + Send + 'static { fn unwatch(&mut self, path: &Path) -> io::Result<()>; } +/// Vfs equivalent to [`std::fs::DirEntry`][std::fs::DirEntry]. +/// +/// [std::fs::DirEntry]: https://doc.rust-lang.org/stable/std/fs/struct.DirEntry.html pub struct DirEntry { path: PathBuf, } @@ -67,6 +74,9 @@ impl DirEntry { } } +/// Vfs equivalent to [`std::fs::ReadDir`][std::fs::ReadDir]. +/// +/// [std::fs::ReadDir]: https://doc.rust-lang.org/stable/std/fs/struct.ReadDir.html pub struct ReadDir { inner: Box>>, } @@ -79,6 +89,9 @@ impl Iterator for ReadDir { } } +/// Vfs equivalent to [`std::fs::Metadata`][std::fs::Metadata]. +/// +/// [std::fs::Metadata]: https://doc.rust-lang.org/stable/std/fs/struct.Metadata.html #[derive(Debug)] pub struct Metadata { is_file: bool, @@ -102,57 +115,55 @@ pub enum VfsEvent { Remove(PathBuf), } -struct VfsLock { +/// Contains implementation details of the Vfs, wrapped by `Vfs` and `VfsLock`, +/// the public interfaces to this type. +struct VfsInner { backend: Box, } -impl VfsLock { - pub fn read>(&mut self, path: P) -> io::Result>> { +impl VfsInner { + fn read>(&mut self, path: P) -> io::Result>> { let path = path.as_ref(); let contents = self.backend.read(path)?; self.backend.watch(path)?; Ok(Arc::new(contents)) } - pub fn write, C: AsRef<[u8]>>( - &mut self, - path: P, - contents: C, - ) -> io::Result<()> { + fn write, C: AsRef<[u8]>>(&mut self, path: P, contents: C) -> io::Result<()> { let path = path.as_ref(); let contents = contents.as_ref(); self.backend.write(path, contents) } - pub fn read_dir>(&mut self, path: P) -> io::Result { + fn read_dir>(&mut self, path: P) -> io::Result { let path = path.as_ref(); let dir = self.backend.read_dir(path)?; self.backend.watch(path)?; Ok(dir) } - pub fn remove_file>(&mut self, path: P) -> io::Result<()> { + fn remove_file>(&mut self, path: P) -> io::Result<()> { let path = path.as_ref(); let _ = self.backend.unwatch(path); self.backend.remove_file(path) } - pub fn remove_dir_all>(&mut self, path: P) -> io::Result<()> { + fn remove_dir_all>(&mut self, path: P) -> io::Result<()> { let path = path.as_ref(); let _ = self.backend.unwatch(path); self.backend.remove_dir_all(path) } - pub fn metadata>(&mut self, path: P) -> io::Result { + fn metadata>(&mut self, path: P) -> io::Result { let path = path.as_ref(); self.backend.metadata(path) } - pub fn event_receiver(&self) -> crossbeam_channel::Receiver { + fn event_receiver(&self) -> crossbeam_channel::Receiver { self.backend.event_receiver() } - pub fn commit_event(&mut self, event: &VfsEvent) -> io::Result<()> { + fn commit_event(&mut self, event: &VfsEvent) -> io::Result<()> { match event { VfsEvent::Remove(path) => { let _ = self.backend.unwatch(&path); @@ -166,7 +177,7 @@ impl VfsLock { /// A virtual filesystem with a configurable backend. pub struct Vfs { - inner: Mutex, + inner: Mutex, } impl Vfs { @@ -177,7 +188,7 @@ impl Vfs { /// Creates a new `Vfs` with the given backend. pub fn new(backend: B) -> Self { - let lock = VfsLock { + let lock = VfsInner { backend: Box::new(backend), }; @@ -186,12 +197,19 @@ impl Vfs { } } + pub fn lock(&self) -> VfsLock<'_> { + VfsLock { + inner: self.inner.lock().unwrap(), + } + } + /// Read a file from the VFS, or the underlying backend if it isn't /// resident. /// /// Roughly equivalent to [`std::fs::read`][std::fs::read]. /// /// [std::fs::read]: https://doc.rust-lang.org/stable/std/fs/fn.read.html + #[inline] pub fn read>(&self, path: P) -> io::Result>> { let path = path.as_ref(); self.inner.lock().unwrap().read(path) @@ -202,6 +220,7 @@ impl Vfs { /// Roughly equivalent to [`std::fs::write`][std::fs::write]. /// /// [std::fs::write]: https://doc.rust-lang.org/stable/std/fs/fn.write.html + #[inline] pub fn write, C: AsRef<[u8]>>(&self, path: P, contents: C) -> io::Result<()> { let path = path.as_ref(); let contents = contents.as_ref(); @@ -213,6 +232,7 @@ impl Vfs { /// Roughly equivalent to [`std::fs::read_dir`][std::fs::read_dir]. /// /// [std::fs::read_dir]: https://doc.rust-lang.org/stable/std/fs/fn.read_dir.html + #[inline] pub fn read_dir>(&self, path: P) -> io::Result { let path = path.as_ref(); self.inner.lock().unwrap().read_dir(path) @@ -223,6 +243,7 @@ impl Vfs { /// Roughly equivalent to [`std::fs::remove_file`][std::fs::remove_file]. /// /// [std::fs::remove_file]: https://doc.rust-lang.org/stable/std/fs/fn.remove_file.html + #[inline] pub fn remove_file>(&self, path: P) -> io::Result<()> { let path = path.as_ref(); self.inner.lock().unwrap().remove_file(path) @@ -233,6 +254,7 @@ impl Vfs { /// Roughly equivalent to [`std::fs::remove_dir_all`][std::fs::remove_dir_all]. /// /// [std::fs::remove_dir_all]: https://doc.rust-lang.org/stable/std/fs/fn.remove_dir_all.html + #[inline] pub fn remove_dir_all>(&self, path: P) -> io::Result<()> { let path = path.as_ref(); self.inner.lock().unwrap().remove_dir_all(path) @@ -243,18 +265,112 @@ impl Vfs { /// Roughly equivalent to [`std::fs::metadata`][std::fs::metadata]. /// /// [std::fs::metadata]: https://doc.rust-lang.org/stable/std/fs/fn.metadata.html + #[inline] pub fn metadata>(&self, path: P) -> io::Result { let path = path.as_ref(); self.inner.lock().unwrap().metadata(path) } /// Retrieve a handle to the event receiver for this `Vfs`. + #[inline] pub fn event_receiver(&self) -> crossbeam_channel::Receiver { self.inner.lock().unwrap().event_receiver() } /// Commit an event to this `Vfs`. + #[inline] pub fn commit_event(&self, event: &VfsEvent) -> io::Result<()> { self.inner.lock().unwrap().commit_event(event) } } + +/// A locked handle to a `Vfs`, created by `Vfs::lock`. +pub struct VfsLock<'a> { + inner: MutexGuard<'a, VfsInner>, +} + +impl VfsLock<'_> { + /// Read a file from the VFS, or the underlying backend if it isn't + /// resident. + /// + /// Roughly equivalent to [`std::fs::read`][std::fs::read]. + /// + /// [std::fs::read]: https://doc.rust-lang.org/stable/std/fs/fn.read.html + #[inline] + pub fn read>(&mut self, path: P) -> io::Result>> { + let path = path.as_ref(); + self.inner.read(path) + } + + /// Write a file to the VFS and the underlying backend. + /// + /// Roughly equivalent to [`std::fs::write`][std::fs::write]. + /// + /// [std::fs::write]: https://doc.rust-lang.org/stable/std/fs/fn.write.html + #[inline] + pub fn write, C: AsRef<[u8]>>( + &mut self, + path: P, + contents: C, + ) -> io::Result<()> { + let path = path.as_ref(); + let contents = contents.as_ref(); + self.inner.write(path, contents) + } + + /// Read all of the children of a directory. + /// + /// Roughly equivalent to [`std::fs::read_dir`][std::fs::read_dir]. + /// + /// [std::fs::read_dir]: https://doc.rust-lang.org/stable/std/fs/fn.read_dir.html + #[inline] + pub fn read_dir>(&mut self, path: P) -> io::Result { + let path = path.as_ref(); + self.inner.read_dir(path) + } + + /// Remove a file. + /// + /// Roughly equivalent to [`std::fs::remove_file`][std::fs::remove_file]. + /// + /// [std::fs::remove_file]: https://doc.rust-lang.org/stable/std/fs/fn.remove_file.html + #[inline] + pub fn remove_file>(&mut self, path: P) -> io::Result<()> { + let path = path.as_ref(); + self.inner.remove_file(path) + } + + /// Remove a directory and all of its descendants. + /// + /// Roughly equivalent to [`std::fs::remove_dir_all`][std::fs::remove_dir_all]. + /// + /// [std::fs::remove_dir_all]: https://doc.rust-lang.org/stable/std/fs/fn.remove_dir_all.html + #[inline] + pub fn remove_dir_all>(&mut self, path: P) -> io::Result<()> { + let path = path.as_ref(); + self.inner.remove_dir_all(path) + } + + /// Query metadata about the given path. + /// + /// Roughly equivalent to [`std::fs::metadata`][std::fs::metadata]. + /// + /// [std::fs::metadata]: https://doc.rust-lang.org/stable/std/fs/fn.metadata.html + #[inline] + pub fn metadata>(&mut self, path: P) -> io::Result { + let path = path.as_ref(); + self.inner.metadata(path) + } + + /// Retrieve a handle to the event receiver for this `Vfs`. + #[inline] + pub fn event_receiver(&self) -> crossbeam_channel::Receiver { + self.inner.event_receiver() + } + + /// Commit an event to this `Vfs`. + #[inline] + pub fn commit_event(&mut self, event: &VfsEvent) -> io::Result<()> { + self.inner.commit_event(event) + } +} diff --git a/vfs/src/memory_backend.rs b/vfs/src/memory_backend.rs index 4fe27269..f1b66788 100644 --- a/vfs/src/memory_backend.rs +++ b/vfs/src/memory_backend.rs @@ -4,9 +4,9 @@ use std::path::{Path, PathBuf}; use crate::{Metadata, ReadDir, VfsBackend, VfsEvent, VfsSnapshot}; -/// `VfsBackend` that reads from an in-memory filesystem. +/// `VfsBackend` that reads from an in-memory filesystem, intended for setting +/// up testing scenarios quickly. #[derive(Debug)] -#[non_exhaustive] pub struct MemoryBackend { entries: HashMap, orphans: BTreeSet,