mod noop_backend; mod std_backend; use std::io; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; pub use noop_backend::NoopBackend; pub use std_backend::StdBackend; mod sealed { use super::*; pub trait Sealed {} impl Sealed for NoopBackend {} impl Sealed for StdBackend {} } /// Backend that can be used to create a /// /// This trait is sealed and cannot not be implemented outside this crate. pub trait VfsBackend: sealed::Sealed { fn read(&mut self, path: &Path) -> io::Result>; fn write(&mut self, path: &Path, data: &[u8]) -> io::Result<()>; fn read_dir(&mut self, path: &Path) -> io::Result; fn metadata(&mut self, path: &Path) -> io::Result; fn event_receiver(&mut self) -> crossbeam_channel::Receiver; fn watch(&mut self, path: &Path) -> io::Result<()>; fn unwatch(&mut self, path: &Path) -> io::Result<()>; } pub struct DirEntry { path: PathBuf, } impl DirEntry { pub fn path(&self) -> &Path { &self.path } } pub struct ReadDir { inner: Box>>, } impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option { self.inner.next() } } #[derive(Debug)] pub struct Metadata { is_file: bool, } impl Metadata { pub fn is_file(&self) -> bool { self.is_file } pub fn is_dir(&self) -> bool { !self.is_file } } #[derive(Debug)] #[non_exhaustive] pub enum VfsEvent { Create(PathBuf), Write(PathBuf), Remove(PathBuf), } struct VfsLock { backend: Box, } impl VfsLock { pub fn read>(&mut self, path: P) -> io::Result>> { let path = path.as_ref(); let contents = self.backend.read(path)?; Ok(Arc::new(contents)) } 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.backend.write(path, contents) } pub fn read_dir>(&mut self, path: P) -> io::Result { let path = path.as_ref(); self.backend.read_dir(path) } } /// A virtual filesystem with a configurable backend. pub struct Vfs { inner: Mutex, } impl Vfs { /// Creates a new `Vfs` with the default backend, `StdBackend`. pub fn new_default() -> Self { Self::new(StdBackend::new()) } /// Creates a new `Vfs` with the given backend. pub fn new(backend: B) -> Self { let lock = VfsLock { backend: Box::new(backend), }; Self { inner: Mutex::new(lock), } } /// 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 pub fn read>(&self, path: P) -> io::Result>> { let path = path.as_ref(); self.inner.lock().unwrap().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 pub fn write, C: AsRef<[u8]>>(&self, path: P, contents: C) -> io::Result<()> { let path = path.as_ref(); let contents = contents.as_ref(); self.inner.lock().unwrap().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 pub fn read_dir>(&self, path: P) -> io::Result { let path = path.as_ref(); self.inner.lock().unwrap().read_dir(path) } }