From e29bd50de878f8a3699c63f2eb9d071061990a33 Mon Sep 17 00:00:00 2001 From: Johannes Hackel Date: Mon, 2 May 2022 18:04:47 +0000 Subject: [PATCH] Add file metadata to AssetIo (#2123) This is a replacement for #2106 This adds a `Metadata` struct which contains metadata information about a file, at the moment only the file type. It also adds a `get_metadata` to `AssetIo` trait and an `asset_io` accessor method to `AssetServer` and `LoadContext` I am not sure about the changes in `AndroidAssetIo ` and `WasmAssetIo`. --- crates/bevy_asset/src/asset_server.rs | 15 ++-- crates/bevy_asset/src/io/android_asset_io.rs | 17 ++++- crates/bevy_asset/src/io/file_asset_io.rs | 17 ++++- crates/bevy_asset/src/io/metadata.rs | 77 ++++++++++++++++++++ crates/bevy_asset/src/io/mod.rs | 20 ++++- crates/bevy_asset/src/io/wasm_asset_io.rs | 21 +++++- crates/bevy_asset/src/loader.rs | 4 + examples/asset/custom_asset_io.rs | 12 +-- 8 files changed, 158 insertions(+), 25 deletions(-) create mode 100644 crates/bevy_asset/src/io/metadata.rs diff --git a/crates/bevy_asset/src/asset_server.rs b/crates/bevy_asset/src/asset_server.rs index 808f502faf..04d1f3bda0 100644 --- a/crates/bevy_asset/src/asset_server.rs +++ b/crates/bevy_asset/src/asset_server.rs @@ -124,7 +124,7 @@ impl AssetServer { /// Enable watching of the filesystem for changes, if support is available, starting from after /// the point of calling this function. pub fn watch_for_changes(&self) -> Result<(), AssetServerError> { - self.server.asset_io.watch_for_changes()?; + self.asset_io().watch_for_changes()?; Ok(()) } @@ -301,7 +301,7 @@ impl AssetServer { }; // load the asset bytes - let bytes = match self.server.asset_io.load_path(asset_path.path()).await { + let bytes = match self.asset_io().load_path(asset_path.path()).await { Ok(bytes) => bytes, Err(err) => { set_asset_failed(); @@ -313,7 +313,7 @@ impl AssetServer { let mut load_context = LoadContext::new( asset_path.path(), &self.server.asset_ref_counter.channel, - &*self.server.asset_io, + self.asset_io(), version, &self.server.task_pool, ); @@ -361,8 +361,7 @@ impl AssetServer { } } - self.server - .asset_io + self.asset_io() .watch_path_for_changes(asset_path.path()) .unwrap(); self.create_assets_in_load_context(&mut load_context); @@ -403,15 +402,15 @@ impl AssetServer { path: P, ) -> Result, AssetServerError> { let path = path.as_ref(); - if !self.server.asset_io.is_directory(path) { + if !self.asset_io().is_dir(path) { return Err(AssetServerError::AssetFolderNotADirectory( path.to_str().unwrap().to_string(), )); } let mut handles = Vec::new(); - for child_path in self.server.asset_io.read_directory(path.as_ref())? { - if self.server.asset_io.is_directory(&child_path) { + for child_path in self.asset_io().read_directory(path.as_ref())? { + if self.asset_io().is_dir(&child_path) { handles.extend(self.load_folder(&child_path)?); } else { if self.get_path_asset_loader(&child_path).is_err() { diff --git a/crates/bevy_asset/src/io/android_asset_io.rs b/crates/bevy_asset/src/io/android_asset_io.rs index 4397dcae63..fc049b77a9 100644 --- a/crates/bevy_asset/src/io/android_asset_io.rs +++ b/crates/bevy_asset/src/io/android_asset_io.rs @@ -1,7 +1,8 @@ -use crate::{AssetIo, AssetIoError}; +use crate::{AssetIo, AssetIoError, Metadata}; use anyhow::Result; use bevy_utils::BoxedFuture; use std::{ + convert::TryFrom, ffi::CString, path::{Path, PathBuf}, }; @@ -46,7 +47,17 @@ impl AssetIo for AndroidAssetIo { Ok(()) } - fn is_directory(&self, path: &Path) -> bool { - self.root_path.join(path).is_dir() + fn get_metadata(&self, path: &Path) -> Result { + let full_path = self.root_path.join(path); + full_path + .metadata() + .and_then(Metadata::try_from) + .map_err(|e| { + if e.kind() == std::io::ErrorKind::NotFound { + AssetIoError::NotFound(full_path) + } else { + e.into() + } + }) } } diff --git a/crates/bevy_asset/src/io/file_asset_io.rs b/crates/bevy_asset/src/io/file_asset_io.rs index 1b5b1630b2..14dcf0a153 100644 --- a/crates/bevy_asset/src/io/file_asset_io.rs +++ b/crates/bevy_asset/src/io/file_asset_io.rs @@ -1,6 +1,6 @@ #[cfg(feature = "filesystem_watcher")] use crate::{filesystem_watcher::FilesystemWatcher, AssetServer}; -use crate::{AssetIo, AssetIoError}; +use crate::{AssetIo, AssetIoError, Metadata}; use anyhow::Result; #[cfg(feature = "filesystem_watcher")] use bevy_ecs::system::Res; @@ -15,6 +15,7 @@ use parking_lot::RwLock; #[cfg(feature = "filesystem_watcher")] use std::sync::Arc; use std::{ + convert::TryFrom, env, fs, io::Read, path::{Path, PathBuf}, @@ -128,8 +129,18 @@ impl AssetIo for FileAssetIo { Ok(()) } - fn is_directory(&self, path: &Path) -> bool { - self.root_path.join(path).is_dir() + fn get_metadata(&self, path: &Path) -> Result { + let full_path = self.root_path.join(path); + full_path + .metadata() + .and_then(Metadata::try_from) + .map_err(|e| { + if e.kind() == std::io::ErrorKind::NotFound { + AssetIoError::NotFound(full_path) + } else { + e.into() + } + }) } } diff --git a/crates/bevy_asset/src/io/metadata.rs b/crates/bevy_asset/src/io/metadata.rs new file mode 100644 index 0000000000..67158a1ccf --- /dev/null +++ b/crates/bevy_asset/src/io/metadata.rs @@ -0,0 +1,77 @@ +use std::convert::{TryFrom, TryInto}; + +/// A enum representing a type of file. +#[non_exhaustive] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum FileType { + Directory, + File, +} + +impl FileType { + #[inline] + pub const fn is_dir(&self) -> bool { + matches!(self, Self::Directory) + } + + #[inline] + pub const fn is_file(&self) -> bool { + matches!(self, Self::File) + } +} + +impl TryFrom for FileType { + type Error = std::io::Error; + + fn try_from(file_type: std::fs::FileType) -> Result { + if file_type.is_dir() { + Ok(Self::Directory) + } else if file_type.is_file() { + Ok(Self::File) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "unknown file type", + )) + } + } +} + +/// Metadata information about a file. +/// +/// This structure is returned from the [`AssetIo::get_metadata`](crate::AssetIo) method. +#[derive(Debug, Clone)] +pub struct Metadata { + file_type: FileType, +} + +impl Metadata { + pub fn new(file_type: FileType) -> Self { + Self { file_type } + } + + #[inline] + pub const fn file_type(&self) -> FileType { + self.file_type + } + + #[inline] + pub const fn is_dir(&self) -> bool { + self.file_type.is_dir() + } + + #[inline] + pub const fn is_file(&self) -> bool { + self.file_type.is_file() + } +} + +impl TryFrom for Metadata { + type Error = std::io::Error; + + fn try_from(metadata: std::fs::Metadata) -> Result { + Ok(Self { + file_type: metadata.file_type().try_into()?, + }) + } +} diff --git a/crates/bevy_asset/src/io/mod.rs b/crates/bevy_asset/src/io/mod.rs index acff0ec013..e3d3e24047 100644 --- a/crates/bevy_asset/src/io/mod.rs +++ b/crates/bevy_asset/src/io/mod.rs @@ -5,6 +5,8 @@ mod file_asset_io; #[cfg(target_arch = "wasm32")] mod wasm_asset_io; +mod metadata; + #[cfg(target_os = "android")] pub use android_asset_io::*; #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))] @@ -12,6 +14,8 @@ pub use file_asset_io::*; #[cfg(target_arch = "wasm32")] pub use wasm_asset_io::*; +pub use metadata::*; + use anyhow::Result; use bevy_utils::BoxedFuture; use downcast_rs::{impl_downcast, Downcast}; @@ -39,9 +43,23 @@ pub trait AssetIo: Downcast + Send + Sync + 'static { &self, path: &Path, ) -> Result>, AssetIoError>; - fn is_directory(&self, path: &Path) -> bool; + fn get_metadata(&self, path: &Path) -> Result; fn watch_path_for_changes(&self, path: &Path) -> Result<(), AssetIoError>; fn watch_for_changes(&self) -> Result<(), AssetIoError>; + + fn is_dir(&self, path: &Path) -> bool { + self.get_metadata(path) + .as_ref() + .map(Metadata::is_dir) + .unwrap_or(false) + } + + fn is_file(&self, path: &Path) -> bool { + self.get_metadata(path) + .as_ref() + .map(Metadata::is_file) + .unwrap_or(false) + } } impl_downcast!(AssetIo); diff --git a/crates/bevy_asset/src/io/wasm_asset_io.rs b/crates/bevy_asset/src/io/wasm_asset_io.rs index a4e260d6fe..4e0d812738 100644 --- a/crates/bevy_asset/src/io/wasm_asset_io.rs +++ b/crates/bevy_asset/src/io/wasm_asset_io.rs @@ -1,8 +1,11 @@ -use crate::{AssetIo, AssetIoError}; +use crate::{AssetIo, AssetIoError, Metadata}; use anyhow::Result; use bevy_utils::BoxedFuture; use js_sys::Uint8Array; -use std::path::{Path, PathBuf}; +use std::{ + convert::TryFrom, + path::{Path, PathBuf}, +}; use wasm_bindgen::JsCast; use wasm_bindgen_futures::JsFuture; use web_sys::Response; @@ -50,7 +53,17 @@ impl AssetIo for WasmAssetIo { Ok(()) } - fn is_directory(&self, path: &Path) -> bool { - self.root_path.join(path).is_dir() + fn get_metadata(&self, path: &Path) -> Result { + let full_path = self.root_path.join(path); + full_path + .metadata() + .and_then(Metadata::try_from) + .map_err(|e| { + if e.kind() == std::io::ErrorKind::NotFound { + AssetIoError::NotFound(full_path) + } else { + e.into() + } + }) } } diff --git a/crates/bevy_asset/src/loader.rs b/crates/bevy_asset/src/loader.rs index ac68f3c6d0..5a5de9b8c1 100644 --- a/crates/bevy_asset/src/loader.rs +++ b/crates/bevy_asset/src/loader.rs @@ -147,6 +147,10 @@ impl<'a> LoadContext<'a> { pub fn task_pool(&self) -> &TaskPool { self.task_pool } + + pub fn asset_io(&self) -> &dyn AssetIo { + self.asset_io + } } /// The result of loading an asset of type `T` diff --git a/examples/asset/custom_asset_io.rs b/examples/asset/custom_asset_io.rs index 941cee61f7..af74a93d2b 100644 --- a/examples/asset/custom_asset_io.rs +++ b/examples/asset/custom_asset_io.rs @@ -1,5 +1,5 @@ use bevy::{ - asset::{AssetIo, AssetIoError}, + asset::{AssetIo, AssetIoError, Metadata}, prelude::*, utils::BoxedFuture, }; @@ -26,11 +26,6 @@ impl AssetIo for CustomAssetIo { self.0.read_directory(path) } - fn is_directory(&self, path: &Path) -> bool { - info!("is_directory({:?})", path); - self.0.is_directory(path) - } - fn watch_path_for_changes(&self, path: &Path) -> Result<(), AssetIoError> { info!("watch_path_for_changes({:?})", path); self.0.watch_path_for_changes(path) @@ -40,6 +35,11 @@ impl AssetIo for CustomAssetIo { info!("watch_for_changes()"); self.0.watch_for_changes() } + + fn get_metadata(&self, path: &Path) -> Result { + info!("get_metadata({:?})", path); + self.0.get_metadata(path) + } } /// A plugin used to execute the override of the asset io