Skip to content

Commit

Permalink
support file operations in single threaded context (#10312)
Browse files Browse the repository at this point in the history
# Objective

- Fixes #10209 
- Assets should work in single threaded

## Solution

- In single threaded mode, don't use `async_fs` but fallback on
`std::fs` with a thin layer to mimic the async API
- file `file_asset.rs` is the async imps from `mod.rs`
- file `sync_file_asset.rs` is the same with `async_fs` APIs replaced by
`std::fs`
- which module is used depends on the `multi-threaded` feature

---------

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
  • Loading branch information
mockersf and cart authored Nov 3, 2023
1 parent fd232ad commit 49bc6cf
Show file tree
Hide file tree
Showing 3 changed files with 533 additions and 228 deletions.
231 changes: 231 additions & 0 deletions crates/bevy_asset/src/io/file/file_asset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
use crate::io::{
get_meta_path, AssetReader, AssetReaderError, AssetWriter, AssetWriterError, PathStream,
Reader, Writer,
};
use async_fs::{read_dir, File};
use bevy_utils::BoxedFuture;
use futures_lite::StreamExt;

use std::path::Path;

use super::{FileAssetReader, FileAssetWriter};

impl AssetReader for FileAssetReader {
fn read<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, Result<Box<Reader<'a>>, AssetReaderError>> {
Box::pin(async move {
let full_path = self.root_path.join(path);
match File::open(&full_path).await {
Ok(file) => {
let reader: Box<Reader> = Box::new(file);
Ok(reader)
}
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
Err(AssetReaderError::NotFound(full_path))
} else {
Err(e.into())
}
}
}
})
}

fn read_meta<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, Result<Box<Reader<'a>>, AssetReaderError>> {
let meta_path = get_meta_path(path);
Box::pin(async move {
let full_path = self.root_path.join(meta_path);
match File::open(&full_path).await {
Ok(file) => {
let reader: Box<Reader> = Box::new(file);
Ok(reader)
}
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
Err(AssetReaderError::NotFound(full_path))
} else {
Err(e.into())
}
}
}
})
}

fn read_directory<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, Result<Box<PathStream>, AssetReaderError>> {
Box::pin(async move {
let full_path = self.root_path.join(path);
match read_dir(&full_path).await {
Ok(read_dir) => {
let root_path = self.root_path.clone();
let mapped_stream = read_dir.filter_map(move |f| {
f.ok().and_then(|dir_entry| {
let path = dir_entry.path();
// filter out meta files as they are not considered assets
if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
if ext.eq_ignore_ascii_case("meta") {
return None;
}
}
let relative_path = path.strip_prefix(&root_path).unwrap();
Some(relative_path.to_owned())
})
});
let read_dir: Box<PathStream> = Box::new(mapped_stream);
Ok(read_dir)
}
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
Err(AssetReaderError::NotFound(full_path))
} else {
Err(e.into())
}
}
}
})
}

fn is_directory<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, std::result::Result<bool, AssetReaderError>> {
Box::pin(async move {
let full_path = self.root_path.join(path);
let metadata = full_path
.metadata()
.map_err(|_e| AssetReaderError::NotFound(path.to_owned()))?;
Ok(metadata.file_type().is_dir())
})
}
}

impl AssetWriter for FileAssetWriter {
fn write<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, Result<Box<Writer>, AssetWriterError>> {
Box::pin(async move {
let full_path = self.root_path.join(path);
if let Some(parent) = full_path.parent() {
async_fs::create_dir_all(parent).await?;
}
let file = File::create(&full_path).await?;
let writer: Box<Writer> = Box::new(file);
Ok(writer)
})
}

fn write_meta<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, Result<Box<Writer>, AssetWriterError>> {
Box::pin(async move {
let meta_path = get_meta_path(path);
let full_path = self.root_path.join(meta_path);
if let Some(parent) = full_path.parent() {
async_fs::create_dir_all(parent).await?;
}
let file = File::create(&full_path).await?;
let writer: Box<Writer> = Box::new(file);
Ok(writer)
})
}

fn remove<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, std::result::Result<(), AssetWriterError>> {
Box::pin(async move {
let full_path = self.root_path.join(path);
async_fs::remove_file(full_path).await?;
Ok(())
})
}

fn remove_meta<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, std::result::Result<(), AssetWriterError>> {
Box::pin(async move {
let meta_path = get_meta_path(path);
let full_path = self.root_path.join(meta_path);
async_fs::remove_file(full_path).await?;
Ok(())
})
}

fn remove_directory<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, std::result::Result<(), AssetWriterError>> {
Box::pin(async move {
let full_path = self.root_path.join(path);
async_fs::remove_dir_all(full_path).await?;
Ok(())
})
}

fn remove_empty_directory<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, std::result::Result<(), AssetWriterError>> {
Box::pin(async move {
let full_path = self.root_path.join(path);
async_fs::remove_dir(full_path).await?;
Ok(())
})
}

fn remove_assets_in_directory<'a>(
&'a self,
path: &'a Path,
) -> BoxedFuture<'a, std::result::Result<(), AssetWriterError>> {
Box::pin(async move {
let full_path = self.root_path.join(path);
async_fs::remove_dir_all(&full_path).await?;
async_fs::create_dir_all(&full_path).await?;
Ok(())
})
}

fn rename<'a>(
&'a self,
old_path: &'a Path,
new_path: &'a Path,
) -> BoxedFuture<'a, std::result::Result<(), AssetWriterError>> {
Box::pin(async move {
let full_old_path = self.root_path.join(old_path);
let full_new_path = self.root_path.join(new_path);
if let Some(parent) = full_new_path.parent() {
async_fs::create_dir_all(parent).await?;
}
async_fs::rename(full_old_path, full_new_path).await?;
Ok(())
})
}

fn rename_meta<'a>(
&'a self,
old_path: &'a Path,
new_path: &'a Path,
) -> BoxedFuture<'a, std::result::Result<(), AssetWriterError>> {
Box::pin(async move {
let old_meta_path = get_meta_path(old_path);
let new_meta_path = get_meta_path(new_path);
let full_old_path = self.root_path.join(old_meta_path);
let full_new_path = self.root_path.join(new_meta_path);
if let Some(parent) = full_new_path.parent() {
async_fs::create_dir_all(parent).await?;
}
async_fs::rename(full_old_path, full_new_path).await?;
Ok(())
})
}
}
Loading

0 comments on commit 49bc6cf

Please sign in to comment.