GithubHelp home page GithubHelp logo

rust-vfs's Introduction

rust-vfs

Crate API Minimum rustc version Actions Status

A virtual filesystem for Rust

The virtual file system abstraction generalizes over file systems and allows using different filesystem implementations (e.g. an in memory implementation for unit tests)

This crate currently has the following implementations:

  • PhysicalFS - the actual filesystem of the underlying OS
  • MemoryFS - an ephemeral in-memory file system, intended mainly for unit tests
  • AltrootFS - a file system with its root in a particular directory of another filesystem
  • OverlayFS - an overlay file system combining two filesystems, an upper layer with read/write access and a lower layer with only read access
  • EmbeddedFS - a read-only file system embedded in the executable, requires embedded-fs feature, no async version available

The minimum supported Rust version (MSRV) is 1.63.

Comments and pull-requests welcome!

Changelog

0.12.0 (2024-03-09)

  • Allow reading and setting modification/creation/access-times - thanks @kartonrad!
  • Allow seek when writing - thanks @jonmclean!

0.11.0 (2024-02-18)

  • Updated minimum supported Rust version to 1.63.
  • Updated rust-embed dependency to 8.0 - thanks @NickAcPT!
  • Unlocked tokio crate version to work with newer versions - thanks @Fredrik-Reinholdsen!
  • use Arc<str> for paths internally to reduce string allocations - thanks @BrettMayson!

0.10.0 (2023-09-08)

  • Added async port of the crate, in a new module async_vfs. The module is behind the async-vfs feature flag which is not enabled by default. Huge thank you to @Fredrik Reinholdsen!
  • Ported all synchronous tests and doc-tests to async
  • Updated minimum supported Rust version to 1.61.0, needed for the async port.
  • Updated Rust edition from 2018 to 2021, needed for the async port.
  • Updated Rust versions used in CI pipeline.

0.9.0 (2022-12-20)

  • prevent Path::create_dir_all() failures when executing in parallel (fixes #47)
  • Allow absolute paths (e.g. starting with "/") in VfsPath::join() (#45 - thanks @Property404)
  • Allow multiple consecutive slashes in paths (#43 - thanks @Property404)
  • Add method VfsPath::is_root() (#44 - thanks @Property404)
  • Path::join() now allows resolving '..' at the root (resolving to root itself) (#41 - thanks @Property404)
  • Add Send to trait objects returned from APIs (#40, #46 - thanks @Property404)

0.8.0 (2022-11-24)

  • Impl std::error::Error for VfsError (#32) and improved error ergonomics for end users (#34) - thanks @Technohacker

0.7.1 (2022-04-15)

  • Fixed a panic when accessing non-existing paths in MemoryFS::append_file() (closes #31)

0.7.0 (2022-03-26)

  • Update to EmbeddedFS to rust-embed v6 (closes #29)
  • Make OverlayFS and AltrootFS available at the crate root, making it more consistent (PR #30 - thanks @Zyian)

0.6.2 (2022-03-07)

  • Activate embedded-fs feature when building on docs.rs to ensure that it actually shows up there (#28 - thanks @Absolucy)

0.6.1 (2022-03-06)

  • Added VfsPath::root() method to access the root path of a virtual filesystem (closes #26)
  • Added doctests to VfsPath docs to provide usage examples

0.6.0 (2022-03-02)

  • Fixed path inconsistency issues in EmbeddedFS (closes #24)
  • Added the test macro test_vfs_readonly! which allows verifying read-only filesystem implementations
  • Removed dependency on thiserror crate to improve compile times (closes #25)

0.5.2 (2022-02-07)

  • Removed potential panic in OverlayFS (closes #23)
  • VfsPath::join() now takes AsRef instead of &str to improve ergonomics with crates like camino

0.5.1 (2021-02-13)

  • Exported test_vfs macro via the feature flag export-test-macros to allow downstream implementations to verify expected behaviour
  • The MSRV is now 1.40 due to requirements in upstream crates
  • The embedded implementation was broken by the 0.5.0 API changes, and is now fixed

0.5.0 (2021-02-13)

  • Added EmbeddedFS for using filesystems embeded in the binary using rust-embed (PR #12 - thanks @ahouts)
  • Changed VfsPath::exists() to return VfsResult<bool> instead of plain bool (closes #17)

0.4.0 (2020-08-13)

  • Added OverlayFS union filesystem
  • Added VfsPath::read_to_string() convenience method
  • Added VfsPath::walk_dir() method for recursive directory traversal
  • Added VfsPath::{copy,move}_{file,dir}() methods (closes #9)
  • License is now Apache 2.0
  • Minimum supported Rust version (MSRV) is 1.32.0

0.3.0 (2020-08-04)

  • Refactored to use a trait based design, simplifying usage and testing

0.2.1 (2020-02-06)

0.1.0 (2016-05-14)

  • Initial release

Roadmap

  • Support for read-only filesystems
  • Support for re-mounting filesystems
  • Support for virtual filesystem access inside archives (e.g. zip)

rust-vfs's People

Contributors

absolucy avatar ahouts avatar brettmayson avatar fredrik-reinholdsen avatar icefoxen avatar jonmclean avatar kartonrad avatar manuel-woelker avatar marius851000 avatar matklad avatar nickacpt avatar property404 avatar technohacker avatar zyian avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

rust-vfs's Issues

Feature: Seek + Write from VfsPath

I'm working on a union crate for this one and rusqlite that allows it to be used as the underlying storage. I have a branch that implements “random access”, which is vital for the SQLite implementation. I can see this being implemented in a way that would be optional for file systems that didn't support such an operation.

What are some things that are missing or could break here that'd prevent this from being made mainline? I could also see this being implemented as a trait for underlying VfsPathfile systems that also implement a means of producing random access files, so it'd fail at compile time.

(Originally published at: https://jacky.wtf/2022/7/lGgU)

Q: Can you run a process in the Vfs Context?

So far this project works great in regards that I can build the file system that I want to surface to a program by using an OverlayFS to combine all of the directories together. But I want to be able to execute a process on the machine in the local context of this new OverlayFS. So that the process thinks that the files are local to it.

Would this usecase be considered out of scope for this project?

no_std compatiblity

Hi!

I am looking for a generic file system abstraction which can possible be re-used with something like https://github.com/littlefs-project/littlefs / https://docs.rs/littlefs/latest/littlefs/ . My goal was to write components which depend on a generic file system abstraction which can be used on larger system like embedded Linux, but also on microcontroller which might not have a full run-time and use a custom or vendor-supplied filesystem.

Do you have any plans to add no_std support for your crate in the future? From what I have seen from the generic FS trait https://docs.rs/vfs/latest/vfs/filesystem/trait.FileSystem.html , alloc support is defnitely required, but there is also a dependency on std::io traits like Read and Write. These trait might move into core soon: rust-lang/rust#48331 , but those traits not being in core is what probably makes this feature non-trivial right now..

Kind Regards
Robin

`VfsError` & `VfsResult` are incompatible with `anyhow`

From 0.6.0, VfsResult cannot be unwrapped using the ? operator in functions that return anyhow::Result.
Due to this, I cannot help using the version 0.5.2.

error[E0277]: the trait bound `VfsError: std::error::Error` is not satisfied
  --> tests\zip_output_test.rs:20:49
   |
20 |     let out_zip_vpath = vfs_root.join("out.zip")?;
   |                                                 ^ the trait `std::error::Error` is not implemented for `VfsError`
   |
   = help: the following other types implement trait `FromResidual<R>`:
             <Result<T, F> as FromResidual<Result<Infallible, E>>>
             <Result<T, F> as FromResidual<Yeet<E>>>
   = note: required because of the requirements on the impl of `From<VfsError>` for `anyhow::Error`
   = note: required because of the requirements on the impl of `FromResidual<Result<Infallible, VfsError>>` for `Result<(), anyhow::Error>`

I think that it is a regression and due to #25.

Questions about future features

I'm writing a game engine that needs a VFS layer (similar to physfs) and this crate caught my eye, but it would need a couple more features to be applicable.

  • Virtual roots, so you could create a VFS rooted in /home/username/.local/game/data and your VFS would essentially use that as its root directory
  • Overlays, so you could effectively merge a filesystem tree with a zip file tree

Would you be interested in accepting pull requests that implement these feature for VFS, or do they not fit with your goals?

create_dir_all is not thread safe/races with itself.

the relevant section of create_dir_all (https://github.com/manuel-woelker/rust-vfs/blob/master/src/path.rs#L228):

if !self.fs.fs.exists(directory)? {
  self.fs.fs.create_dir(directory)?;
}

Suppose I have a Vec<(&str, &vfspath)>, and I want to concurrently write the strings to the paths, which may or may not exist. Naively, one approach is to run create_dir_all on the paths' parents and then write out to each path. But doing that can race because thread A creates a common ancestor path between the check and the create call from thread B.

Sort of similar to rust-lang/rust#33707

Export macro test_vfs

If the vfs is extended by in another crate as suggested here, than the macro test_vfs! can't be used from the other crate, since it is only available for the tests. It would be nice, if this macro can be exported for the use within other crates. My be it can be exported as a feature?

At least, I didn't found any possibility to access the macro from an other crate.

Readonly Filesystem

Separate the FileSystem trait and AynscFileSystem trait into ReadOnlyFileSystem and WriteOnlyFileSystem.

Q: Why does VfsPath have a ref to the FS?

Hello! I'm one of the core maintainers of the rs-tiled crate, and have been considering this crate for a while to solve a few issues we're having with source paths and accessing virtual Tiled files in virtual filesystems.

However, I have a few questions about the design of the crate, the biggest of which being the nature of VfsPath. Unlike the path types present in std::path, they have an atomic refcount for the FS they are referring to. I don't see why this is: From my point of view, it's inconsistent with the standard types, slower than you would expect and unneccesary, since you could just pass a reference to the VFS the path is from. What am I missing here?

BTW: I can see that this crate has a lot of effort put into it and I'd love to see it reach 1.0.0! :) If necessary, I'd be able to help it reach this milestone by enforcing the Rust API Guidelines.

`MemoryFS::new()` panics in webassembly

Hi. I was hoping to use vfs as an abstraction so that a lib designed for use with std::fs (that really just uses the filesystem for simple input) could be used from webassembly. I went through and adjusted everything to go through vfs instead, but then in the webassembly build I got a panic on MemoryFS::new() 😭.

Any idea why this would be and if it could be fixed?

Have VfsPath::join() treat paths more unix-like

Example, these are invalid:
path.root().join("..") - this should just return the root
path.join("/") - This should also return the root (this is how Path::join works)
path.join("a///b") - This should be the same as path.join("a/b") (this is also how Path::join works)

Do you think it's a good idea to change the behavior of VfsPath::join()? It would certainly be more convenient for what I'm working on.

I implemented the first suggestion in #41

Let me know and I can submit more PRs

Resolving . to current directory

I'd like a way to use the physical filesystem and be able to access files using absolute paths while also using . or path/to/file for paths relative to my current directory, similar to how I'd use with any std::fs method, but I don't see a way to do it in PhysicalFS, in fact it specifically protects against it in the get_path method.

For a bit of context, I'm making a tool that reads configurations and source files from current directory and writes to files across the user's home directory (actually the user can configure any absolute path).
I want all my filesystem access to go through a global dyn FileSystem, that would usually be a PhysicalFS, then, in unit tests I want to switch it out for a MemoryFS and just run the tool against that.

A workaround I could try would be to prepend the current directory to each path, but I feel like that's pretty unergonomic.

Adding is_dir() and is_file() to VfsPath

It would be nice, if VfsPath has a method is_dir() and is_file() similar to the implementation of std::path::PathBuf.

At the moment, one has to write something similar to this:

path.metadata()?.file_type == VfsFileType::Directory

Which I find inconvenient.

AltrootFS panics when file does not exist yet

Minimal repro:

let fs = vfs::altroot::AltrootFS::new("/tmp");
fs.path("dummy");

If /tmp/dummy does not exist on your filesystem, the above code panics here:

full_path.canonicalize().unwrap();

It seems that canonicalize requires the path to exist, so if the file does not exist yet, then we panic.

I assume this is not the expected behavior, because I think calling .path is the one and only way to create a new file, but right now, AltrootFS panics before I can create the file.

Idea: support tar or zip files

It would be cool to write Rust code that could accept a &dyn vfs::filesystem::FileSystem argument and then read data from a variety of file system formats. You already have AltrootFS, but it would be nifty if there were other implementations like TarFS or ZipFS that read paths from a tar or zip file. This would enable library authors to write functions that could read from either a flat directory or from a tar/zip file directly.

Incompatibility with rust-embed ^6.0

Hi,

I am looking to use this library in an ongoing project -- specifically, I am hoping to use the EmbeddedFS features to make an embedded, portable virtual file system for python and WASM ports of my robotics library.

I noticed that using new versions of rust-embed are not compatible with rust-vfs. Specifically, using rust-embed versions ^6.0 causes "the trait rust_embed::RustEmbed is not implemented for ..." errors, even when the struct explicitly derives from RustEmbed. To be clear, this issue is not present for rust-embed versions 5.9 and below; however, it would be great if versions 6.0 and above could be supported since there are really nice features in the newer versions of rust-embed (compression, include-exclude, etc.).

Just let me know if you need any more details, thanks a lot!

Idea: Implement a VFS based on GitHub Gist

Hi!

I am currently working on a little project, which is (yet another) todo list, that can be maintained in the local filesystem or in a private GitHub gist. Recently, I thought of introducing a kind of an abstraction layer, a VFS. Then I stumbled upon this crate!

Would you be open to the idea to add a gist based VFS?

If so, I would be happy to allocate some time for this and come back to you with a PR. But tbh. the implementation will have some limitations: gists are flat (no directories permitted, only files). So to make it work the operations create_dir etc. will need to be moved to another trait (HierarchicalFs vs. FlatFs) or produce a runtime error...

A consistent definition for paths + EmbeddedFS quirk

Hello!
I'm in the process of building an implementation of FileSystem for my own use, where I can mount other VFS implementations under a single root. Having some documentation on the FileSystem trait on the sort of paths that the implementation is expected to support would be a huge boon

A specific situation I am dealing with at the moment is the root folder. EmbeddedFS has a quirk with read_dir where:

  1. On an instance of EmbeddedFS, I can read_dir("/") and have a successful Result from it
  2. After conversion of the EmbeddedFS instance to a VfsPath directly without any join()s, path.read_dir() fails with an Error, saying it cannot read ""

You can test this with:

EmbeddedFS::<RustEmbedRoot>::new().read_dir("/").unwrap().for_each(|x| println!("{}", x.as_str()));

// and

let p: VfsPath = EmbeddedFS::<RustEmbedRoot>::new().into();
p.read_dir().unwrap();

With the current implementation of VfsPath::join, I can't do path.join("/") which is alright, but I'd love to have a consistent definition :)

VfsError does not have an easy way to programmatically match against Error Variants

Hey again!
Due to the existence of the VfsError::WithContext variant, it's harder to match on a specific error type since there may be multiple nested WithContext error levels to go through. My use case for this is to ignore an error if a file is missing, but to handle all other cases as invalid situations

There is also the situation of certain implementations returning errors like NotFound via std::io::Error rather than the VfsError variants, but I believe that's an implementation specific issue

To work around this, I currently have a function called get_root_cause that returns the true root cause of a VfsError, which also normalizes I/O error types to their respective VfsError variant

fn get_root_cause(err: VfsError) -> VfsError {
    match err {
        VfsError::WithContext { cause, .. } => get_root_cause(*cause),
        VfsError::IoError(ref io_err) => match io_err.kind() {
            io::ErrorKind::NotFound => {
                VfsError::FileNotFound { path: "".into() }
            }
            _ => err,
        },
        _ => err,
    }
}

If this fix is alright, I can make a PR for the same. But I wanted to have some discussion on how to proceed with this bug in case there were other solutions available

Accessing the VfsPath::fs

I think, I found another issue with the VfsPath.

Let assume, that we want to read some path from a configuration file. Therefore we write a function with a header similar to this:

fn read_path_from_config(config_path: VfsPath) -> VfsResult<VfsPath> {
    // do open the file
    // read path property from config and store it in a String
    let path : String = read_property()?;
    // here we have the problem, that the path is valid in the filesystem in config_path, but we can't access
    // this filesystem and therefore we can't construct a result_path with the type VfsPath, which is in the same 
    // filesystem
    Ok(result_path);
}

It would be possible, to delete the path in config_path by calling parent() until it returns None and than use join to put path into config_path. But I find this wired.

I think, VfsPath is missing some methods to construct a new path from an already existing path to keep the filesystem.

What do you think?

Feature: Merging / flattening Filesystems into each other

I have tried to use this library for transactional filesystem operations; specifically, when my project is usually invoked, when using stdlib file system operations, intermediate artifacts could be left on the physical filesystem in the case of an error.

To avoid this, I use the vfs::OverlayFS with a read-only vfs::PhysicalFS and a vfs::MemoryFS with read/write access.
This is great because all modifications to the filesystem end up in the vfs::MemoryFS, i.e. all changes induced by the operation can be found there.
These changes, ultimately have to be stored and should be applied to the vfs::PhysicalFS for persistence.

let phys_loc: Path = ...;
let (update_fs, read_fs) = (
    vfs::MemoryFS::new(), 
    vfs::PhysicalFS::new(&phys_loc)
);
let proj_fs = vfs::OverlayFS::new(&[
    update_fs.into(), 
    read_fs.into()
]);

{
// Transaction begins here; 
// Failure triggers the error path, terminating a transaction prior to the next method.
// Success means the expected changes have been written into the `update_fs` by virtue of `vfs::OverlayFS`
fsys_ops1(&proj_fs)?;
fsys_ops2(&proj_fs)?;
fsys_ops3(&proj_fs)?;

// When we reach here, all operations were successfully, and the
// results should now be persisted 
update_fs.persist_into(&read_fs)?;
}

The final method call of persist_into is what this feature request is all about; being able to persist the contents of a memory only filesystem into a physical one. Perhaps this could even be extended to a trait implementation, e.g.

pub trait FileSystem {
    fn persist_into(self, fs: &vfs::PhysicalFS) -> VfsResult<()>;
}

Apologies if this falls outside of the scope of this library :)
I hope I have adequately described my idea for a feature request.

EmbeddedFS File Not Found

My code compiles properly but it seems like there is some sort of issue with rust-embed as the fs says file not found when compiled with wasm-pack

I am on the latest version of this repo and using rust-embed 8.0.0 to match this repos requirements.

Here's the relevant code:

#[derive(RustEmbed)]
#[derive(Debug)]
#[folder = "./templates"]
struct EmbedFFS;
let EFS: VfsPath = EmbeddedFS::<EmbedFFS>::new().into();
let im: VfsPath = EFS.join("templates").unwrap();

When I call: im.is_dir().unwrap() I get false

Could this be a build issue?
Im running this to build: wasm-pack build --target web --release

Cannot easily copy directory to root of new filesystem

Ran into this while creating a zip writing filesystem. If the zip filesystem starts out assuming "" exists, copy_dir immediately fails (despite the fact that it's empty). Alternatively, if the root filesystem must be created, get_parent fails slightly later. Possible fixes include:

  • Making get_parent on None successful?
  • Allowing empty destination directories?
  • Allowing merges with a destination directory?
  • Some combination thereof?

Relevant code

rust-vfs/src/path.rs

Lines 400 to 406 in c34f4ca

pub fn copy_dir(&self, destination: &VfsPath) -> VfsResult<u64> {
let mut files_copied = 0u64;
|| -> VfsResult<()> {
if destination.exists() {
return Err("Destination exists already".to_string().into());
}
destination.create_dir()?;

rust-vfs/src/path.rs

Lines 130 to 131 in c34f4ca

pub fn create_dir(&self) -> VfsResult<()> {
self.get_parent("create directory")?;

rust-vfs/src/path.rs

Lines 182 to 191 in c34f4ca

fn get_parent(&self, action: &str) -> VfsResult<()> {
let parent = self.parent();
match parent {
None => {
return Err(format!(
"Could not {} at '{}', not a valid location",
action, &self.path
)
.into());
}

vfs-zip/examples/copy-early-vfs-zip.rs lines 5-10:
NOTE: Current example code succeeds because I copy to a subdirectory.

fn main() {
    create_dir_all("target/tmp").unwrap();;
    let src = VfsPath::new(ZipReadOnly::new_strict(File::open("test/data/early-vfs-zip.zip").unwrap()).unwrap());
    let dst = VfsPath::new(ZipWriteOnly::new_weak(File::create("target/tmp/early-vfs-zip-copy.zip").unwrap()).unwrap());
    // NOTE: https://github.com/MaulingMonkey/vfs-zip/issues/1
    let copied = src.copy_dir(&dst.join("subdir").unwrap()).unwrap();

Implement a FileSystem backed by S3

We need an S3 backed virtual file system for a few applications we've created over at Optact. This project seemed like a good opportunity to contribute to something that is already in use, rather than creating something of our own.

If this an interesting addition to the project, we'll create a draft PR soon.

  • Implement AsyncFileSystem
  • Implement FileSystem
  • Make this implementation a feature with proper dependencies for S3 and existing async feature
  • Figure out a sane way to create unit tests for this feature
  • Update readme

move_file shouldn't require the destination to not exist

The documentation on the move_file method states "The destination must not exist, but its parent directory must".

This doesn't align with the behavior of the built-in fs::rename method (which the PhysicalFS calls to do this).

Should this library align with the lower-level behavior? Without the same behavior, you cannot do an atomic rename, something I need.

Add ability to create intermediate directories, like mkdirp

I was trying to create a file a/b/c.json in a new MemoryFS, but I got the following error:

Custom { kind: Other, error: "File not found \"b\"" }

It would be nice if it could auto-create the intermediate directories. mkdir -p does this when creating directories, for example.

Method for moving files/dirs

It doesn't look like there's any method for moving/renaming files and directories currently. You can delete and recreate files/dirs, but the performance of this can be bad for large files/dirs.

vfs-zip, async I/O, and future plans

Hello! I've created vfs-zip, allowing vfs to read from .zip files, and thought I'd share a few questions and meandering thoughts.

  • Would you be interested in async APIs? Browsers lack blocking I/O, so the current abstractions don't really work if you wanted to write a browser-compatible HttpFS - instead you'd need to use something entirely loaded up-front like MemoryFS or vfs_zip::ZipReadOnly<std::io::Cursor<Vec<u8>>>.

  • It'd be nice to get rid of the boxes in trait FileSystem by extending it to have something like:

    type ReadDirEntry : AsRef<str> + Into<String> + Display;
    type ReadDir : Iterator<Item = Self::ReadDirEntry>;
    fn read_dir(&self, path: &str) -> VfsResult<Self::ReadDir>;

    This would eliminate some virtual dispatch overhead if you wanted to have FileSystems wrapping other FileSystems, or if you wanted to directly use &impl FileSystems. Box<dyn FileSystem> and/or Rc<dyn FileSystem> could implement FileSystem with the existing boxes.

  • It'd be nice to relax the Send+Sync requirements of FileSystem. vfs-zip (and likely other archive readers) are currently forced to resort to mutexes... I suppose that's a natural consequence of VfsPath internally storing an Arc<Box<dyn FileSystem>> though.

I'm also vaguely interested in writing more VFS abstractions for:

  • Generic servers (GET / PUT over HTTP(S)? SSH so I can talk to a mac mini or linux vm from my windows machine?)
  • VCS servers (it'd be nice to be able to treat, say, a git commit or perforce revision as a filesystem to read from the server on-demand)
  • On-demand filesystems (e.g. requesting some/game/shader.bin might compile some/game/shader.hlsl on the fly)
  • Multi-disk caching filesystems - e.g. writing foo/bar might copy/move files from C:\SddCacheD:\SlowDiskCacheE:\BiggerSlowerDiskCache//SomeNAS/Cache - possibly deleting/evicting files that haven't been used in awhile based on configuration files / size limits / file extensions.

I'm unlikely to write this all at once, or anytime soon, but thought I'd share what I'm considering tackling, check what (if anything) can/should be upstreamed into vfs itself, and make sure nobody's about to beat me to the punch.

Use camino paths or support non-UTF-8 paths?

Hi there -- thanks for writing this crate! I'd like to use it for some of my own crates that interact with the file system.

I'm wondering if you think using camino for paths would be interesting -- since you already restrict yourself to paths that can be encoded as UTF-8.

Alternatively, you could also go in the direction of supporting non-UTF-8 paths by using Path, OsString etc.

(I also noticed some path handling that might be funky on Windows, such as potentially not handling backslash path separators properly -- either camino or std::path::Path will help with that since they internally handle those separators.)

Why is a `'static` lifetime required for things that `impl FileSystem`?

The core FileSystem trait is defined as:

pub trait FileSystem: Debug + Sync + Send + 'static

This prohibits someone from implementing this trait on something that requires borrowed data like so:

#[derive(Debug)]
struct StFS<'stfs, 'data> {
	package: &'stfs StfsPackage,
	data: &'data [u8],
}

impl<'stfs, 'data> FileSystem for StFS<'stfs, 'data> {
    // ...
}

From a cursory glance it seems like this is required because there's a Box<dyn Filesystem> involved with VfsPath -- which makes sense, but if I don't want to use VfsPath then this isn't necessarily a problem.

The following patch removes these restrictions:

diff --git a/src/filesystem.rs b/src/filesystem.rs
index 6124348..dce3e55 100644
--- a/src/filesystem.rs
+++ b/src/filesystem.rs
@@ -12,7 +12,7 @@ use std::time::SystemTime;
 /// Path components may be any UTF-8 string, except "/", "." and ".."
 ///
 /// Please use the test_macros [test_macros::test_vfs!] and [test_macros::test_vfs_readonly!]
-pub trait FileSystem: Debug + Sync + Send + 'static {
+pub trait FileSystem: Debug + Sync + Send {
     /// Iterates over all direct children of this directory path
     /// NOTE: the returned String items denote the local bare filenames, i.e. they should not contain "/" anywhere
     fn read_dir(&self, path: &str) -> VfsResult<Box<dyn Iterator<Item = String> + Send>>;
@@ -60,7 +60,7 @@ pub trait FileSystem: Debug + Sync + Send + 'static {
     }
 }
 
-impl<T: FileSystem> From<T> for VfsPath {
+impl<T: FileSystem + 'static> From<T> for VfsPath {
     fn from(filesystem: T) -> Self {
         VfsPath::new(filesystem)
     }
diff --git a/src/path.rs b/src/path.rs
index b6d7d45..7306223 100644
--- a/src/path.rs
+++ b/src/path.rs
@@ -141,7 +141,7 @@ impl VfsPath {
     /// # use vfs::{PhysicalFS, VfsPath};
     /// let path = VfsPath::new(PhysicalFS::new("."));
     /// ````
-    pub fn new<T: FileSystem>(filesystem: T) -> Self {
+    pub fn new<T: FileSystem + 'static>(filesystem: T) -> Self {
         VfsPath {
             path: "".into(),
             fs: Arc::new(VFS {
@@ -736,7 +736,7 @@ impl VfsPath {
     /// ```
     pub fn walk_dir(&self) -> VfsResult<WalkDirIterator> {
         Ok(WalkDirIterator {
-            inner: Box::new(self.read_dir()?),
+            inner: self.read_dir()?,
             todo: vec![],
         })
     }

Also note in VfsPath::walk_dir() I remove the Box::new() which isn't necessary, but the value was being double-boxed.

Would this patch be accepted or is there something I'm missing that makes lifting this restriction a bad idea?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.