bevy_assets_extensions 0.7.0

Extensions for bevy assets, with support of collection as assets and a loader manager.
Documentation
use std::{collections::HashMap, pin::Pin};

use bevy::prelude::*;

use super::{BundleEntry, LoadAssetFrom};
use crate::prelude::*;

/// Convenient macro to call load_from
#[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;

/// Convenient macro to call load_handle_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;
/// Wrapper type used for the specialization of CallLoadAssetFrom.
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> {
    /// Create an empty wrapper.
    pub fn new() -> Self {
        Self {
            phantom_data: Default::default(),
        }
    }
}

/// Wrapper type used for the specialization of CallLoadAssetHandleFrom.
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>> {
    /// Create an empty wrapper.
    pub fn new() -> Self {
        Self {
            phantom_data: Default::default(),
        }
    }
}

/// Trait for supporting to load from a hash map of bundle entry
pub trait CallLoadAssetFrom {
    /// Type of asset outputted by this CallLoadAssetFrom implementation
    type Target;
    /// Load asset from an entry.
    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)
    }
}

/// Generic implementation
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(String::new(), 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 }),
            }
        })
    }
}
//  _   _           _     __  __
// | | | | __ _ ___| |__ |  \/  | __ _ _ __
// | |_| |/ _` / __| '_ \| |\/| |/ _` | '_ \
// |  _  | (_| \__ \ | | | |  | | (_| | |_) |
// |_| |_|\__,_|___/_| |_|_|  |_|\__,_| .__/
//                                    |_|

impl<A: Asset + std::fmt::Debug + LoadAssetFrom> CallLoadAssetFrom
    for &&&CallLoadFromWrapper<HashMap<String, Handle<A>>>
{
    type Target = HashMap<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 = HashMap::<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 + std::fmt::Debug> CallLoadAssetFrom
    for &&CallLoadFromWrapper<HashMap<String, Handle<A>>>
{
    type Target = HashMap<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 = HashMap::<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<HashMap<String, A>> {
    type Target = HashMap<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 = HashMap::<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 }),
            }
        })
    }
}

// __     __
// \ \   / /__  ___
//  \ \ / / _ \/ __|
//   \ V /  __/ (__
//    \_/ \___|\___|
//

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 }),
            }
        })
    }
}