diff --git a/crates/bevy_asset/src/io/file/file_asset.rs b/crates/bevy_asset/src/io/file/file_asset.rs new file mode 100644 index 0000000000000..b33a2c80bb0e7 --- /dev/null +++ b/crates/bevy_asset/src/io/file/file_asset.rs @@ -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>, AssetReaderError>> { + Box::pin(async move { + let full_path = self.root_path.join(path); + match File::open(&full_path).await { + Ok(file) => { + let reader: Box = 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>, 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 = 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, 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 = 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> { + 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, 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 = Box::new(file); + Ok(writer) + }) + } + + fn write_meta<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture<'a, Result, 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 = 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(()) + }) + } +} diff --git a/crates/bevy_asset/src/io/file/mod.rs b/crates/bevy_asset/src/io/file/mod.rs index 629fd7dd9c659..aae016df3bf1d 100644 --- a/crates/bevy_asset/src/io/file/mod.rs +++ b/crates/bevy_asset/src/io/file/mod.rs @@ -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>, AssetReaderError>> { - Box::pin(async move { - let full_path = self.root_path.join(path); - match File::open(&full_path).await { - Ok(file) => { - let reader: Box = 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>, 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 = 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, 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 = 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> { - 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, 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 = Box::new(file); - Ok(writer) - }) - } - - fn write_meta<'a>( - &'a self, - path: &'a Path, - ) -> BoxedFuture<'a, Result, 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 = 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(()) - }) - } -} diff --git a/crates/bevy_asset/src/io/file/sync_file_asset.rs b/crates/bevy_asset/src/io/file/sync_file_asset.rs new file mode 100644 index 0000000000000..a8bf573a7ab07 --- /dev/null +++ b/crates/bevy_asset/src/io/file/sync_file_asset.rs @@ -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> { + 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> { + 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> { + 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> { + Poll::Ready(Ok(())) + } +} + +struct DirReader(Vec); + +impl Stream for DirReader { + type Item = PathBuf; + + fn poll_next( + self: Pin<&mut Self>, + _cx: &mut std::task::Context<'_>, + ) -> Poll> { + 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>, AssetReaderError>> { + Box::pin(async move { + let full_path = self.root_path.join(path); + match File::open(&full_path) { + Ok(file) => { + let reader: Box = 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>, 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 = 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, 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 = 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> { + 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, 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 = Box::new(FileWriter(file)); + Ok(writer) + }) + } + + fn write_meta<'a>( + &'a self, + path: &'a Path, + ) -> BoxedFuture<'a, Result, 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 = 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(()) + }) + } +}