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`.
This commit is contained in:
Johannes Hackel 2022-05-02 18:04:47 +00:00
parent 8283db69b4
commit e29bd50de8
8 changed files with 158 additions and 25 deletions

View file

@ -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<Vec<HandleUntyped>, 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() {

View file

@ -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<Metadata, AssetIoError> {
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()
}
})
}
}

View file

@ -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<Metadata, AssetIoError> {
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()
}
})
}
}

View file

@ -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<std::fs::FileType> for FileType {
type Error = std::io::Error;
fn try_from(file_type: std::fs::FileType) -> Result<Self, Self::Error> {
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<std::fs::Metadata> for Metadata {
type Error = std::io::Error;
fn try_from(metadata: std::fs::Metadata) -> Result<Self, Self::Error> {
Ok(Self {
file_type: metadata.file_type().try_into()?,
})
}
}

View file

@ -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<Box<dyn Iterator<Item = PathBuf>>, AssetIoError>;
fn is_directory(&self, path: &Path) -> bool;
fn get_metadata(&self, path: &Path) -> Result<Metadata, AssetIoError>;
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);

View file

@ -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<Metadata, AssetIoError> {
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()
}
})
}
}

View file

@ -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`

View file

@ -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<Metadata, AssetIoError> {
info!("get_metadata({:?})", path);
self.0.get_metadata(path)
}
}
/// A plugin used to execute the override of the asset io