rojo/vfs/src/std_backend.rs
Lucien Greathouse 477e0ada32
VFS in external crate (#297)
* vroom

* Port dir middleware

* Filter rules

* Directory metadata

* Project support

* Enable Lua support

* StringValue support

* CSV

* rbxm, rbxmx, and rbxlx

* JSON models

* Clean up some warnings

* Strip out PathMap

* Unwatch paths when they're reported as removed

* Fix 'rojo upload' behavior

* Upgrade to Insta 0.13.1

* Update dependencies

* Release 0.6.0-alpha.2

* Fix bad merge

* Replace MemoryBackend with InMemoryFs

* Sledgehammer tests into passing for now

* Txt middleware

* Update easy snapshot tests

* Lua tests

* Project middleware tests

* Try to fix test failures by sorting

* Port first set of serve session tests

* Add InMemoryFs::raise_event

* Finish porting serve session tests

* Remove UI code for introspecting VFS for now

* VFS docs
2020-03-10 17:38:49 -07:00

111 lines
3.1 KiB
Rust

use std::fs;
use std::io;
use std::path::Path;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use crossbeam_channel::Receiver;
use notify::{watcher, DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
use crate::{DirEntry, Metadata, ReadDir, VfsBackend, VfsEvent};
/// `VfsBackend` that uses `std::fs` and the `notify` crate.
pub struct StdBackend {
watcher: RecommendedWatcher,
watcher_receiver: Receiver<VfsEvent>,
}
impl StdBackend {
pub fn new() -> StdBackend {
let (notify_tx, notify_rx) = mpsc::channel();
let watcher = watcher(notify_tx, Duration::from_millis(50)).unwrap();
let (tx, rx) = crossbeam_channel::unbounded();
thread::spawn(move || {
for event in notify_rx {
match event {
DebouncedEvent::Create(path) => {
tx.send(VfsEvent::Create(path))?;
}
DebouncedEvent::Write(path) => {
tx.send(VfsEvent::Write(path))?;
}
DebouncedEvent::Remove(path) => {
tx.send(VfsEvent::Remove(path))?;
}
DebouncedEvent::Rename(from, to) => {
tx.send(VfsEvent::Remove(from))?;
tx.send(VfsEvent::Create(to))?;
}
_ => {}
}
}
Result::<(), crossbeam_channel::SendError<VfsEvent>>::Ok(())
});
Self {
watcher,
watcher_receiver: rx,
}
}
}
impl VfsBackend for StdBackend {
fn read(&mut self, path: &Path) -> io::Result<Vec<u8>> {
fs::read(path)
}
fn write(&mut self, path: &Path, data: &[u8]) -> io::Result<()> {
fs::write(path, data)
}
fn read_dir(&mut self, path: &Path) -> io::Result<ReadDir> {
let entries: Result<Vec<_>, _> = fs::read_dir(path)?.collect();
let mut entries = entries?;
entries.sort_by_cached_key(|entry| entry.file_name());
let inner = entries
.into_iter()
.map(|entry| Ok(DirEntry { path: entry.path() }));
Ok(ReadDir {
inner: Box::new(inner),
})
}
fn remove_file(&mut self, path: &Path) -> io::Result<()> {
fs::remove_file(path)
}
fn remove_dir_all(&mut self, path: &Path) -> io::Result<()> {
fs::remove_dir_all(path)
}
fn metadata(&mut self, path: &Path) -> io::Result<Metadata> {
let inner = fs::metadata(path)?;
Ok(Metadata {
is_file: inner.is_file(),
})
}
fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
self.watcher_receiver.clone()
}
fn watch(&mut self, path: &Path) -> io::Result<()> {
self.watcher
.watch(path, RecursiveMode::NonRecursive)
.map_err(|inner| io::Error::new(io::ErrorKind::Other, inner))
}
fn unwatch(&mut self, path: &Path) -> io::Result<()> {
self.watcher
.unwatch(path)
.map_err(|inner| io::Error::new(io::ErrorKind::Other, inner))
}
}