
# Objective - Fixes #8140 ## Solution - Added Explicit Error Typing for `AssetLoader` and `AssetSaver`, which were the last instances of `anyhow` in use across Bevy. --- ## Changelog - Added an associated type `Error` to `AssetLoader` and `AssetSaver` for use with the `load` and `save` methods respectively. - Changed `ErasedAssetLoader` and `ErasedAssetSaver` `load` and `save` methods to use `Box<dyn Error + Send + Sync + 'static>` to allow for arbitrary `Error` types from the non-erased trait variants. Note the strict requirements match the pre-existing requirements around `anyhow::Error`. ## Migration Guide - `anyhow` is no longer exported by `bevy_asset`; Add it to your own project (if required). - `AssetLoader` and `AssetSaver` have an associated type `Error`; Define an appropriate error type (e.g., using `thiserror`), or use a pre-made error type (e.g., `anyhow::Error`). Note that using `anyhow::Error` is a drop-in replacement. - `AssetLoaderError` has been removed; Define a new error type, or use an alternative (e.g., `anyhow::Error`) - All the first-party `AssetLoader`'s and `AssetSaver`'s now return relevant (and narrow) error types instead of a single ambiguous type; Match over the specific error type, or encapsulate (`Box<dyn>`, `thiserror`, `anyhow`, etc.) ## Notes A simpler PR to resolve this issue would simply define a Bevy `Error` type defined as `Box<dyn std::error::Error + Send + Sync + 'static>`, but I think this type of error handling should be discouraged when possible. Since only 2 traits required the use of `anyhow`, it isn't a substantive body of work to solidify these error types, and remove `anyhow` entirely. End users are still encouraged to use `anyhow` if that is their preferred error handling style. Arguably, adding the `Error` associated type gives more freedom to end-users to decide whether they want more or less explicit error handling (`anyhow` vs `thiserror`). As an aside, I didn't perform any testing on Android or WASM. CI passed locally, but there may be mistakes for those platforms I missed.
82 lines
3.0 KiB
Rust
82 lines
3.0 KiB
Rust
use crate::io::{
|
|
get_meta_path, AssetReader, AssetReaderError, AssetWatcher, EmptyPathStream, PathStream,
|
|
Reader, VecReader,
|
|
};
|
|
use bevy_log::error;
|
|
use bevy_utils::BoxedFuture;
|
|
use std::{ffi::CString, path::Path};
|
|
|
|
/// [`AssetReader`] implementation for Android devices, built on top of Android's [`AssetManager`].
|
|
///
|
|
/// Implementation details:
|
|
///
|
|
/// - [`load_path`](AssetIo::load_path) uses the [`AssetManager`] to load files.
|
|
/// - [`read_directory`](AssetIo::read_directory) always returns an empty iterator.
|
|
/// - Watching for changes is not supported. The watcher method will do nothing.
|
|
///
|
|
/// [AssetManager]: https://developer.android.com/reference/android/content/res/AssetManager
|
|
pub struct AndroidAssetReader;
|
|
|
|
impl AssetReader for AndroidAssetReader {
|
|
fn read<'a>(
|
|
&'a self,
|
|
path: &'a Path,
|
|
) -> BoxedFuture<'a, Result<Box<Reader<'a>>, AssetReaderError>> {
|
|
Box::pin(async move {
|
|
let asset_manager = bevy_winit::ANDROID_APP
|
|
.get()
|
|
.expect("Bevy must be setup with the #[bevy_main] macro on Android")
|
|
.asset_manager();
|
|
let mut opened_asset = asset_manager
|
|
.open(&CString::new(path.to_str().unwrap()).unwrap())
|
|
.ok_or(AssetReaderError::NotFound(path.to_path_buf()))?;
|
|
let bytes = opened_asset.get_buffer()?;
|
|
let reader: Box<Reader> = Box::new(VecReader::new(bytes.to_vec()));
|
|
Ok(reader)
|
|
})
|
|
}
|
|
|
|
fn read_meta<'a>(
|
|
&'a self,
|
|
path: &'a Path,
|
|
) -> BoxedFuture<'a, Result<Box<Reader<'a>>, AssetReaderError>> {
|
|
Box::pin(async move {
|
|
let meta_path = get_meta_path(path);
|
|
let asset_manager = bevy_winit::ANDROID_APP
|
|
.get()
|
|
.expect("Bevy must be setup with the #[bevy_main] macro on Android")
|
|
.asset_manager();
|
|
let mut opened_asset = asset_manager
|
|
.open(&CString::new(meta_path.to_str().unwrap()).unwrap())
|
|
.ok_or(AssetReaderError::NotFound(meta_path))?;
|
|
let bytes = opened_asset.get_buffer()?;
|
|
let reader: Box<Reader> = Box::new(VecReader::new(bytes.to_vec()));
|
|
Ok(reader)
|
|
})
|
|
}
|
|
|
|
fn read_directory<'a>(
|
|
&'a self,
|
|
_path: &'a Path,
|
|
) -> BoxedFuture<'a, Result<Box<PathStream>, AssetReaderError>> {
|
|
let stream: Box<PathStream> = Box::new(EmptyPathStream);
|
|
error!("Reading directories is not supported with the AndroidAssetReader");
|
|
Box::pin(async move { Ok(stream) })
|
|
}
|
|
|
|
fn is_directory<'a>(
|
|
&'a self,
|
|
_path: &'a Path,
|
|
) -> BoxedFuture<'a, std::result::Result<bool, AssetReaderError>> {
|
|
error!("Reading directories is not supported with the AndroidAssetReader");
|
|
Box::pin(async move { Ok(false) })
|
|
}
|
|
|
|
fn watch_for_changes(
|
|
&self,
|
|
_event_sender: crossbeam_channel::Sender<super::AssetSourceEvent>,
|
|
) -> Option<Box<dyn AssetWatcher>> {
|
|
None
|
|
}
|
|
}
|