[github](https://github.com/vivekpanyam/lunchbox) [crates.io](https://crates.io/crates/lunchbox) [docs.rs](https://docs.rs/lunchbox) An async virtual filesystem interface in Rust. Lunchbox provides a common interface that can be used to interact with any filesystem (e.g. a local FS, in-memory FS, zip filesystem, etc). This interface closely matches `tokio::fs::` so it's easy to get started with. High level features: - Support for read only filesystems and read/write filesystems - A built-in implementation for local filesystems - An async interface - WASM support # Getting started Add lunchbox to your Cargo.toml: ```toml [dependencies] lunchbox = "0.1" ``` ## Step 1: Load a filesystem At its core, Lunchbox provides a `ReadableFileSystem` and a `WritableFileSystem` trait. These traits provide analogues for all the functions available in `tokio::fs`. We'll use the built in `LocalFS`, but you can use anything that implements one of the filesystem traits above. ```rust use lunchbox::ReadableFileSystem; use lunchbox::LocalFS; // Create a lunchbox filesystem that uses the root of your local filesystem as its root let local_fs = LocalFS::new()?; // Create a lunchbox filesystem that uses `/tmp` as its root. // `/some/path` inside the filesystem would be translated to `/tmp/some/path` // on your local filesystem let local_fs = LocalFS::with_base_dir("/tmp").await?; ``` *Note: using `LocalFS` requires the `localfs` feature* ## Step 2: Use it instead of `tokio::fs::...` Instead of ```rust tokio::fs::canonicalize("/some/path").await ``` you'd write ```rust local_fs.canonicalize("/some/path").await ``` # Details ## `Path`s We can't use `std::path::Path` directly because it contains methods that directly access the filesystem (e.g. `exists`) and is not portable across OSs (e.g. a path on Windows is different than a path on Unix). As an alternative, we use the [relative_path](https://docs.rs/relative-path/1.7.2/relative_path/index.html) crate to give us platform-independent paths. We also provide an extension trait (`LunchboxPathUtils`) to add methods like `exists`. This is available as `lunchbox::path::Path` and `lunchbox::path::PathBuf`. Methods in the lunchbox traits generally accept anything that implements `AsRef`. This means you can use `str` or `String` directly as you would with standard library paths. See the [relative_path](https://docs.rs/relative-path/1.7.2/relative_path/index.html) docs for more details. ## Trait methods In the below methods, note that `PathType` is just an alias for `AsRef + Send`. `ReadableFileSystem` contains the following methods ```rust // Open a file async fn open(&self, path: impl PathType) -> Result where Self::FileType: ReadableFile; // These are almost identical to tokio::fs::... async fn canonicalize(&self, path: impl PathType) -> Result; async fn metadata(&self, path: impl PathType) -> Result; async fn read(&self, path: impl PathType) -> Result>; async fn read_dir(&self, path: impl PathType) -> Result; async fn read_link(&self, path: impl PathType) -> Result; async fn read_to_string(&self, path: impl PathType) -> Result; async fn symlink_metadata(&self, path: impl PathType) -> Result; ``` `WritableFileSystem` contains ```rust // Create a file async fn create(&self, path: impl PathType) -> Result where Self::FileType: WritableFile; // Open a file with options async fn open_with_opts( &self, opts: &OpenOptions, path: impl PathType, ) -> Result where Self::FileType: WritableFile; // These are almost identical to tokio::fs::... async fn copy(&self, from: impl PathType, to: impl PathType) -> Result; async fn create_dir(&self, path: impl PathType) -> Result<()>; async fn create_dir_all(&self, path: impl PathType) -> Result<()>; async fn hard_link(&self, src: impl PathType, dst: impl PathType) -> Result<()>; async fn remove_dir(&self, path: impl PathType) -> Result<()>; async fn remove_dir_all(&self, path: impl PathType) -> Result<()>; async fn remove_file(&self, path: impl PathType) -> Result<()>; async fn rename(&self, from: impl PathType, to: impl PathType) -> Result<()>; async fn set_permissions(&self, path: impl PathType, perm: Permissions) -> Result<()>; async fn symlink(&self, src: impl PathType, dst: impl PathType) -> Result<()>; async fn write(&self, path: impl PathType, contents: impl AsRef<[u8]> + Send) -> Result<()>; ``` These work with `ReadableFile`s and `WritableFile`s respectively: ```rust /// A readable file #[async_trait] pub trait ReadableFile: AsyncRead where Self: Sized, { // These are almost identical to tokio::fs::File::... async fn metadata(&self) -> Result; async fn try_clone(&self) -> Result; } /// A file that supports both reads and writes #[async_trait] pub trait WritableFile: ReadableFile + AsyncWrite { // These are almost identical to tokio::fs::File::... async fn sync_all(&self) -> Result<()>; async fn sync_data(&self) -> Result<()>; async fn set_len(&self, size: u64) -> Result<()>; async fn set_permissions(&self, perm: Permissions) -> Result<()>; } ``` Note that all `WritableFile`s must be `ReadableFile`s and all `WritableFileSystem`s must be `ReadableFileSystem`s as well. ## WASM For WASM builds, the `Send` and `Sync` bounds are removed from all parts of the interface.