mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
support file operations in single threaded context (#10312)
# 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>
This commit is contained in:
parent
fd232ad360
commit
49bc6cfd62
3 changed files with 533 additions and 228 deletions
231
crates/bevy_asset/src/io/file/file_asset.rs
Normal file
231
crates/bevy_asset/src/io/file/file_asset.rs
Normal file
|
@ -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(())
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,16 +1,14 @@
|
|||
#[cfg(feature = "file_watcher")]
|
||||
mod file_watcher;
|
||||
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
mod file_asset;
|
||||
#[cfg(not(feature = "multi-threaded"))]
|
||||
mod sync_file_asset;
|
||||
|
||||
#[cfg(feature = "file_watcher")]
|
||||
pub use file_watcher::*;
|
||||
|
||||
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::{
|
||||
env,
|
||||
path::{Path, PathBuf},
|
||||
|
@ -72,102 +70,6 @@ impl FileAssetReader {
|
|||
}
|
||||
}
|
||||
|
||||
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())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FileAssetWriter {
|
||||
root_path: PathBuf,
|
||||
}
|
||||
|
@ -183,127 +85,3 @@ impl FileAssetWriter {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
296
crates/bevy_asset/src/io/file/sync_file_asset.rs
Normal file
296
crates/bevy_asset/src/io/file/sync_file_asset.rs
Normal file
|
@ -0,0 +1,296 @@
|
|||
use futures_io::{AsyncRead, AsyncWrite};
|
||||
use futures_lite::Stream;
|
||||
|
||||
use crate::io::{
|
||||
get_meta_path, AssetReader, AssetReaderError, AssetWriter, AssetWriterError, PathStream,
|
||||
Reader, Writer,
|
||||
};
|
||||
use bevy_utils::BoxedFuture;
|
||||
|
||||
use std::{
|
||||
fs::{read_dir, File},
|
||||
io::{Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
pin::Pin,
|
||||
task::Poll,
|
||||
};
|
||||
|
||||
use super::{FileAssetReader, FileAssetWriter};
|
||||
|
||||
struct FileReader(File);
|
||||
|
||||
impl AsyncRead for FileReader {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut std::task::Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> Poll<std::io::Result<usize>> {
|
||||
let this = self.get_mut();
|
||||
let read = this.0.read(buf);
|
||||
Poll::Ready(read)
|
||||
}
|
||||
}
|
||||
|
||||
struct FileWriter(File);
|
||||
|
||||
impl AsyncWrite for FileWriter {
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut std::task::Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<std::io::Result<usize>> {
|
||||
let this = self.get_mut();
|
||||
let wrote = this.0.write(buf);
|
||||
Poll::Ready(wrote)
|
||||
}
|
||||
|
||||
fn poll_flush(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut std::task::Context<'_>,
|
||||
) -> Poll<std::io::Result<()>> {
|
||||
let this = self.get_mut();
|
||||
let flushed = this.0.flush();
|
||||
Poll::Ready(flushed)
|
||||
}
|
||||
|
||||
fn poll_close(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut std::task::Context<'_>,
|
||||
) -> Poll<std::io::Result<()>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
struct DirReader(Vec<PathBuf>);
|
||||
|
||||
impl Stream for DirReader {
|
||||
type Item = PathBuf;
|
||||
|
||||
fn poll_next(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut std::task::Context<'_>,
|
||||
) -> Poll<Option<Self::Item>> {
|
||||
let this = self.get_mut();
|
||||
Poll::Ready(this.0.pop())
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
Ok(file) => {
|
||||
let reader: Box<Reader> = Box::new(FileReader(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) {
|
||||
Ok(file) => {
|
||||
let reader: Box<Reader> = Box::new(FileReader(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) {
|
||||
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(DirReader(mapped_stream.collect()));
|
||||
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() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
let file = File::create(&full_path)?;
|
||||
let writer: Box<Writer> = Box::new(FileWriter(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() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
let file = File::create(&full_path)?;
|
||||
let writer: Box<Writer> = Box::new(FileWriter(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);
|
||||
std::fs::remove_file(full_path)?;
|
||||
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);
|
||||
std::fs::remove_file(full_path)?;
|
||||
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);
|
||||
std::fs::remove_dir_all(full_path)?;
|
||||
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);
|
||||
std::fs::remove_dir(full_path)?;
|
||||
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);
|
||||
std::fs::remove_dir_all(&full_path)?;
|
||||
std::fs::create_dir_all(&full_path)?;
|
||||
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() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
std::fs::rename(full_old_path, full_new_path)?;
|
||||
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() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
std::fs::rename(full_old_path, full_new_path)?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue