use std::pin::Pin;
use bevy::prelude::*;
use super::{BundleEntry, BundleValue, LoadAssetFrom};
use crate::prelude::*;
#[macro_export]
macro_rules! call_load_from {
($A: ty, $key: expr, $entry: expr, $load_context: expr) => {{
#[allow(unused_imports)]
use $crate::assets::bundle::{
CallLoadAssetFrom as _, CallLoadFromWrapper, LoadAssetFrom as _,
};
(&&&&&&CallLoadFromWrapper::<$A>::new()).call_load_from($key, $entry, $load_context)
}};
}
pub use call_load_from;
#[macro_export]
macro_rules! load_handle_from {
($H: ty, $key: expr, $entry: expr, $load_context: expr) => {{
#[allow(unused_imports)]
use $crate::assets::bundle::{
AssetHandleWrapper, CallLoadAssetFrom as _, LoadAssetFrom as _,
};
(&&&AssetHandleWrapper::<$H>::new()).load_handle_from($key, $entry, $load_context)
}};
}
pub use load_handle_from;
pub struct CallLoadFromWrapper<A: ?Sized> {
phantom_data: std::marker::PhantomData<A>,
}
impl<A: ?Sized> Default for CallLoadFromWrapper<A> {
fn default() -> Self {
Self::new()
}
}
impl<A: ?Sized> CallLoadFromWrapper<A> {
pub fn new() -> Self {
Self {
phantom_data: Default::default(),
}
}
}
pub struct AssetHandleWrapper<H: ?Sized> {
phantom_data: std::marker::PhantomData<H>,
}
impl<A: Asset> Default for AssetHandleWrapper<Handle<A>> {
fn default() -> Self {
Self::new()
}
}
impl<A: Asset> AssetHandleWrapper<Handle<A>> {
pub fn new() -> Self {
Self {
phantom_data: Default::default(),
}
}
}
pub trait CallLoadAssetFrom {
type Target;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>>;
}
impl<A: Asset + LoadAssetFrom> CallLoadAssetFrom for &&&CallLoadFromWrapper<A> {
type Target = A;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
A::load_from(key, entry, load_context)
}
}
impl<A: Asset + Sized> CallLoadAssetFrom for &&CallLoadFromWrapper<A> {
type Target = A;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async {
match entry {
BundleEntry::File(path) => {
let path = load_context.asset_path().parent().unwrap().resolve(&path)?;
Ok(load_context.loader().immediate().load(path).await?.take())
}
_ => Err(Error::IncompatibleEntry { key }),
}
})
}
}
impl<A: Asset + LoadAssetFrom> CallLoadAssetFrom for &&&&CallLoadFromWrapper<Handle<A>> {
type Target = Handle<A>;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async {
let a = A::load_from(key, entry, load_context).await?;
Ok(load_context.add_labeled_asset(uuid::Uuid::new_v4().to_string(), a))
})
}
}
impl<A: Asset + Sized> CallLoadAssetFrom for &&&CallLoadFromWrapper<Handle<A>> {
type Target = Handle<A>;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async {
match entry {
BundleEntry::File(path) => {
let path = load_context.asset_path().parent().unwrap().resolve(&path)?;
Ok(load_context.load(path))
}
_ => Err(Error::IncompatibleEntry { key }),
}
})
}
}
macro_rules! impl_hashmap_loaders {
($map:ident) => {
impl<A> CallLoadAssetFrom for &&&&CallLoadFromWrapper<$map<String, Handle<A>>>
where
A: Asset + std::fmt::Debug + LoadAssetFrom,
{
type Target = $map<String, Handle<A>>;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async {
match entry {
BundleEntry::Bundle(assets) => {
let mut m = Self::Target::default();
for (k, v) in assets {
let a = call_load_from!(A, k.to_owned(), v, load_context).await?;
let h = load_context.add_labeled_asset(k.clone(), a);
m.insert(k.clone(), h);
}
Ok(m)
}
_ => Err(Error::IncompatibleEntry { key }),
}
})
}
}
impl<A: Asset + std::fmt::Debug> CallLoadAssetFrom
for &&&CallLoadFromWrapper<$map<String, Handle<A>>>
{
type Target = $map<String, Handle<A>>;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async {
match entry {
BundleEntry::Bundle(assets) => {
let mut m = $map::<String, Handle<A>>::default();
for (k, v) in assets {
let a = call_load_from!(A, k.to_owned(), v, load_context).await?;
let h = load_context.add_labeled_asset(k.clone(), a);
m.insert(k.clone(), h);
}
Ok(m)
}
_ => Err(Error::IncompatibleEntry { key }),
}
})
}
}
impl<A: Asset + LoadAssetFrom> CallLoadAssetFrom
for &&&&CallLoadFromWrapper<$map<String, A>>
{
type Target = $map<String, A>;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async {
match entry {
BundleEntry::Bundle(assets) => {
let mut m = $map::<String, A>::default();
for (k, v) in assets {
m.insert(
k.to_owned(),
call_load_from!(A, k, v, load_context).await?,
);
}
Ok(m)
}
_ => Err(Error::IncompatibleEntry { key }),
}
})
}
}
};
}
use bevy::platform::collections::HashMap as BevyHashMap;
use std::collections::HashMap as StdHashMap;
impl_hashmap_loaders!(StdHashMap);
impl_hashmap_loaders!(BevyHashMap);
impl CallLoadAssetFrom for &&&&CallLoadFromWrapper<Vec<String>> {
type Target = Vec<String>;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
_load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async {
match entry {
BundleEntry::List(assets) => {
let mut m = Vec::<String>::default();
for v in assets {
match v {
BundleEntry::Text(content) => m.push(content),
_ => return Err(Error::IncompatibleEntry { key }),
}
}
Ok(m)
}
BundleEntry::StringList(list) => Ok(list),
_ => Err(Error::IncompatibleEntry { key }),
}
})
}
}
impl CallLoadAssetFrom for &&&&&&CallLoadFromWrapper<f32> {
type Target = f32;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
_load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async move {
match entry {
BundleEntry::Float(f) => Ok(f as f32),
_ => Err(Error::IncompatibleEntry { key }),
}
})
}
}
impl CallLoadAssetFrom for &&&&CallLoadFromWrapper<Color> {
type Target = Color;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
_load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async move {
match entry {
BundleEntry::Color(c) => Ok(c),
_ => Err(Error::IncompatibleEntry { key }),
}
})
}
}
impl CallLoadAssetFrom for &&&&CallLoadFromWrapper<UiRect> {
type Target = UiRect;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
_load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async move {
match entry {
BundleEntry::UiRect(c) => Ok(c),
_ => Err(Error::IncompatibleEntry { key }),
}
})
}
}
impl CallLoadAssetFrom for &&&&CallLoadFromWrapper<TextureSlicer> {
type Target = TextureSlicer;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
_load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async move {
match entry {
BundleEntry::TextureSlicer(c) => Ok(c),
_ => Err(Error::IncompatibleEntry { key }),
}
})
}
}
impl<T> CallLoadAssetFrom for &&CallLoadFromWrapper<Option<T>>
where
for<'a> &'a CallLoadFromWrapper<T>: CallLoadAssetFrom<Target = T>,
{
type Target = Option<T>;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async { Ok(Some(call_load_from!(T, key, entry, load_context).await?)) })
}
}
impl<T> CallLoadAssetFrom for &&&CallLoadFromWrapper<Option<T>>
where
for<'a> &'a &'a CallLoadFromWrapper<T>: CallLoadAssetFrom<Target = T>,
{
type Target = Option<T>;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async { Ok(Some(call_load_from!(T, key, entry, load_context).await?)) })
}
}
impl<T> CallLoadAssetFrom for &&&&CallLoadFromWrapper<Option<T>>
where
for<'a> &'a &'a &'a CallLoadFromWrapper<T>: CallLoadAssetFrom<Target = T>,
{
type Target = Option<T>;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async { Ok(Some(call_load_from!(T, key, entry, load_context).await?)) })
}
}
impl<T> CallLoadAssetFrom for &&&&&CallLoadFromWrapper<Option<T>>
where
for<'a> &'a &'a &'a &'a CallLoadFromWrapper<T>: CallLoadAssetFrom<Target = T>,
{
type Target = Option<T>;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async { Ok(Some(call_load_from!(T, key, entry, load_context).await?)) })
}
}
impl<T> CallLoadAssetFrom for &&&&&&CallLoadFromWrapper<Option<T>>
where
for<'a> &'a &'a &'a &'a &'a CallLoadFromWrapper<T>: CallLoadAssetFrom<Target = T>,
{
type Target = Option<T>;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async { Ok(Some(call_load_from!(T, key, entry, load_context).await?)) })
}
}
impl<T> CallLoadAssetFrom for CallLoadFromWrapper<T>
where
T: serde::de::DeserializeOwned,
{
type Target = T;
fn call_load_from<'a, 's>(
&'s self,
key: String,
entry: BundleEntry,
_load_context: &'a mut bevy::asset::LoadContext<'_>,
) -> Pin<Box<dyn Future<Output = Result<Self::Target, Error>> + Send + 'a>> {
Box::pin(async {
match entry {
BundleEntry::Float(fl) => Ok(BundleValue::Float(fl).deserialize_into()?),
BundleEntry::Text(string) => Ok(BundleValue::Text(string).deserialize_into()?),
BundleEntry::Value(value) => Ok(value.deserialize_into()?),
_ => Err(Error::IncompatibleEntry { key }),
}
})
}
}