Skip to content

Support for async_fs#210

Draft
bitdriftr wants to merge 5 commits intotonbo-io:mainfrom
bitdriftr:asyncfs
Draft

Support for async_fs#210
bitdriftr wants to merge 5 commits intotonbo-io:mainfrom
bitdriftr:asyncfs

Conversation

@bitdriftr
Copy link

Hello, this PR adds a new disk implementation using async-fs. I want to support async-fs because it does not require a Tokio reactor, which I don't have in my current context.

This PR is gated behind a new feature asyncfs.

Let me know if you are interested in this feature. If so, I can work more on the PR to get it upstreamed.

Copilot AI review requested due to automatic review settings October 6, 2025 20:00
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds an optional async-fs based disk backend (feature flag asyncfs) to provide an async file implementation that does not depend on a Tokio reactor.

  • Introduces AsyncFile wrapper and NativeFs implementation using async-fs.
  • Adds feature flag and dependency wiring in Cargo.toml.
  • Re-exports async-fs filesystem types when enabled.

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
fusio/src/impls/disk/mod.rs Adds asyncfs module and re-exports behind feature flag.
fusio/src/impls/disk/asyncfs/mod.rs Implements AsyncFile with Read/Write traits using async-fs.
fusio/src/impls/disk/asyncfs/fs.rs Implements NativeFs (Fs trait) using async-fs for filesystem operations.
fusio/Cargo.toml Adds asyncfs feature and async-fs dependency.

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +52 to +53
let file = self.file.as_mut().expect("close file after closed");
File::close(file).await?;
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The close method does not set self.file to None after closing, so subsequent operations (which use unwrap/expect) will proceed on a closed handle and the panic messages like "write file after closed" will never trigger, leading instead to I/O errors on a logically closed file. Either remove the Option wrapper (if double-close protection is not required) or replace lines 52-53 with something like if let Some(file) = self.file.take() { file.close().await?; } to make the state transition explicit and future unwraps fail predictably.

Suggested change
let file = self.file.as_mut().expect("close file after closed");
File::close(file).await?;
if let Some(file) = self.file.take() {
File::close(&file).await?;
} else {
panic!("close file after closed");
}

Copilot uses AI. Check for mistakes.
} else {
return Err(Error::Path(std::boxed::Box::new(io::Error::new(
io::ErrorKind::NotFound,
"Path not found and option.create is false",
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message says "option.create" but the variable is options; adjust the message to "Path not found and options.create is false" (or reference the actual OpenOptions field name) for clarity.

Suggested change
"Path not found and option.create is false",
"Path not found and options.create is false",

Copilot uses AI. Check for mistakes.
}
}

let absolute_path = std::fs::canonicalize(&local_path).unwrap();
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unwrap() here can panic (e.g. race deletion, permission issues); propagate the error instead to avoid unexpected panics: let absolute_path = std::fs::canonicalize(&local_path).map_err(|e| Error::Path(Box::new(e)))?;.

Suggested change
let absolute_path = std::fs::canonicalize(&local_path).unwrap();
let absolute_path = std::fs::canonicalize(&local_path)
.map_err(|e| Error::Path(Box::new(e)))?;

Copilot uses AI. Check for mistakes.
async fn open_options(&self, path: &Path, options: OpenOptions) -> Result<Self::File, Error> {
let local_path =
path_to_local(path).map_err(|err| Error::Path(std::boxed::Box::new(err)))?;
if !local_path.exists() {
Copy link

Copilot AI Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

local_path.exists() performs a blocking filesystem call in an async context; prefer using async_fs::metadata (and handling the error) to avoid a potential blocking operation on the async executor thread. Example: let exists = async_fs::metadata(&local_path).await.is_ok(); then branch on exists.

Suggested change
if !local_path.exists() {
if !async_fs::metadata(&local_path).await.is_ok() {

Copilot uses AI. Check for mistakes.
@bitdriftr bitdriftr marked this pull request as draft October 6, 2025 20:04
@belveryin belveryin requested a review from ethe October 6, 2025 20:13
@ethe
Copy link
Member

ethe commented Oct 6, 2025

Hi @bitdriftr , I'm absolutely insterested in another backend implementation, I'll take a look once it is ready for review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants