Skip to content

Commit

Permalink
feature: add support for fs_err
Browse files Browse the repository at this point in the history
  • Loading branch information
beckend authored and al8n committed Jul 27, 2024
1 parent 507ffb0 commit e39aef7
Show file tree
Hide file tree
Showing 31 changed files with 1,280 additions and 1,611 deletions.
14 changes: 8 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,27 @@ sync = []
async-std = ["dep:async-std"]
tokio = ["dep:tokio"]
smol = ["dep:smol"]
fs-err = ["dep:fs-err", "fs-err/io_safety"]
fs-err-tokio = ["fs-err", "fs-err/tokio"]

[target.'cfg(not(windows))'.dependencies]
rustix = { version = "0.38", features = ["fs"] }

[target.'cfg(windows)'.dependencies.windows-sys]
version = "0.52"
features = [
"Win32_Foundation",
"Win32_Storage_FileSystem",
"Win32_System_IO"
]
features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_IO"]

[dependencies]
async-std = { version = "1", optional = true }
fs-err = { version = "2", optional = true }
smol = { version = "2", optional = true }
tokio = { version = "1", optional = true, default-features = false, features = ["fs"] }
tokio = { version = "1", optional = true, default-features = false, features = [
"fs",
] }

[dev-dependencies]
async-std = { version = "1", features = ["attributes"] }
fs-err = { version = "2", features = ["io_safety", "tokio"] }
smol-potat = "1.1"
tempdir = "0.3"
tokio = { version = "1", features = ["full"] }
Expand Down
30 changes: 21 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,28 @@ This is a fork of the [fs2-rs](https://github.com/danburkert/fs2-rs) crate, the
</div>

## Installation
- std
- std
```toml
[dependencies]
fs4 = { version = "0.8", features = ["sync"] }
```
- [tokio runtime](https://crates.io/crates/tokio)

- [async-std runtime](https://crates.io/crates/async-std)
```toml
[dependencies]
fs4 = { version = "0.8", features = ["tokio"] }
fs4 = { version = "0.8", features = ["async-std"] }
```

- [async-std runtime](https://crates.io/crates/async-std)
- [fs-err](https://crates.io/crates/fs-err)
```toml
[dependencies]
fs4 = { version = "0.8", features = ["async-std"] }
fs4 = { version = "0.8", features = ["fs-err"] }
```

- [fs-err-tokio](https://crates.io/crates/fs-err)
```toml
[dependencies]
fs4 = { version = "0.8", features = ["fs-err-tokio"] }
```

- [smol runtime](https://crates.io/crates/smol)
Expand All @@ -44,15 +50,22 @@ This is a fork of the [fs2-rs](https://github.com/danburkert/fs2-rs) crate, the
fs4 = { version = "0.8", features = ["smol"] }
```

- [tokio runtime](https://crates.io/crates/tokio)
```toml
[dependencies]
fs4 = { version = "0.8", features = ["tokio"] }
```

## Features

- [x] file locks.
- [x] file (pre)allocation.
- [x] file allocation information.
- [x] filesystem space usage information.
- [x] [tokio support](https://crates.io/crates/tokio)
- [x] [smol support](https://crates.io/crates/smol)
- [x] [async-std support](https://crates.io/crates/async-std)
- [x] [fs-err](https://crates.io/crates/fs-err)
- [x] [smol support](https://crates.io/crates/smol)
- [x] [tokio support](https://crates.io/crates/tokio)

## License

Expand All @@ -71,4 +84,3 @@ Copyright (c) 2015 Dan Burkert.
[doc-url]: https://docs.rs/fs4
[crates-url]: https://crates.io/crates/fs4
[codecov-url]: https://app.codecov.io/gh/al8n/fs4-rs/

3 changes: 3 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[toolchain]
# channel = 'stable'
channel = 'nightly'
3 changes: 1 addition & 2 deletions src/file_ext.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
cfg_sync!(
mod sync_impl;
pub use sync_impl::FileExt;
pub(crate) mod sync_impl;
);

cfg_async!(
Expand Down
180 changes: 179 additions & 1 deletion src/file_ext/async_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ macro_rules! async_file_ext {
/// [`LockFile`](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365202(v=vs.85).aspx)
/// on Windows.
pub trait AsyncFileExt {

/// Returns the amount of physical space allocated for a file.
fn allocated_size(&self) -> impl core::future::Future<Output = Result<u64>>;

Expand Down Expand Up @@ -85,10 +84,189 @@ macro_rules! async_file_ext {
}
}

macro_rules! test_mod {
($annotation:meta, $($use_stmt:item)*) => {
#[cfg(test)]
mod test {
extern crate tempdir;
extern crate test;
use crate::{
allocation_granularity, available_space, free_space,
lock_contended_error, total_space,
};

$(
$use_stmt
)*

/// Tests shared file lock operations.
#[$annotation]
async fn lock_shared() {
let tempdir = tempdir::TempDir::new("fs4").unwrap();
let path = tempdir.path().join("fs4");
let file1 = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.await
.unwrap();
let file2 = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.await
.unwrap();
let file3 = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.await
.unwrap();

// Concurrent shared access is OK, but not shared and exclusive.
file1.lock_shared().unwrap();
file2.lock_shared().unwrap();
assert_eq!(
file3.try_lock_exclusive().unwrap_err().kind(),
lock_contended_error().kind()
);
file1.unlock().unwrap();
assert_eq!(
file3.try_lock_exclusive().unwrap_err().kind(),
lock_contended_error().kind()
);

// Once all shared file locks are dropped, an exclusive lock may be created;
file2.unlock().unwrap();
file3.lock_exclusive().unwrap();
}

/// Tests exclusive file lock operations.
#[$annotation]
async fn lock_exclusive() {
let tempdir = tempdir::TempDir::new("fs4").unwrap();
let path = tempdir.path().join("fs4");
let file1 = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.await
.unwrap();
let file2 = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.await
.unwrap();

// No other access is possible once an exclusive lock is created.
file1.lock_exclusive().unwrap();
assert_eq!(
file2.try_lock_exclusive().unwrap_err().kind(),
lock_contended_error().kind()
);
assert_eq!(
file2.try_lock_shared().unwrap_err().kind(),
lock_contended_error().kind()
);

// Once the exclusive lock is dropped, the second file is able to create a lock.
file1.unlock().unwrap();
file2.lock_exclusive().unwrap();
}

/// Tests that a lock is released after the file that owns it is dropped.
#[$annotation]
async fn lock_cleanup() {
let tempdir = tempdir::TempDir::new("fs4").unwrap();
let path = tempdir.path().join("fs4");
let file1 = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.await
.unwrap();
let file2 = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.await
.unwrap();

file1.lock_exclusive().unwrap();
assert_eq!(
file2.try_lock_shared().unwrap_err().kind(),
lock_contended_error().kind()
);

// Drop file1; the lock should be released.
drop(file1);
file2.lock_shared().unwrap();
}

/// Tests file allocation.
#[$annotation]
async fn allocate() {
let tempdir = tempdir::TempDir::new("fs4").unwrap();
let path = tempdir.path().join("fs4");
let file = fs::OpenOptions::new()
.write(true)
.create(true)
.open(&path)
.await
.unwrap();
let blksize = allocation_granularity(&path).unwrap();

// New files are created with no allocated size.
assert_eq!(0, file.allocated_size().await.unwrap());
assert_eq!(0, file.metadata().await.unwrap().len());

// Allocate space for the file, checking that the allocated size steps
// up by block size, and the file length matches the allocated size.

file.allocate(2 * blksize - 1).await.unwrap();
assert_eq!(2 * blksize, file.allocated_size().await.unwrap());
assert_eq!(2 * blksize - 1, file.metadata().await.unwrap().len());

// Truncate the file, checking that the allocated size steps down by
// block size.

file.set_len(blksize + 1).await.unwrap();
assert_eq!(2 * blksize, file.allocated_size().await.unwrap());
assert_eq!(blksize + 1, file.metadata().await.unwrap().len());
}

/// Checks filesystem space methods.
#[$annotation]
async fn filesystem_space() {
let tempdir = tempdir::TempDir::new("fs4").unwrap();
let total_space = total_space(tempdir.path()).unwrap();
let free_space = free_space(tempdir.path()).unwrap();
let available_space = available_space(tempdir.path()).unwrap();

assert!(total_space > free_space);
assert!(total_space > available_space);
assert!(available_space <= free_space);
}
}
};
}

cfg_async_std! {
pub(crate) mod async_std_impl;
}

cfg_fs_err_tokio! {
pub(crate) mod fs_err_tokio_impl;
}

cfg_smol! {
pub(crate) mod smol_impl;
}
Expand Down
Loading

0 comments on commit e39aef7

Please sign in to comment.