use crate::{
database::path::{AssetPath, AssetPathStatic},
fetch::{AssetAwaitsAsyncFetch, AssetFetch},
};
use anput::{bundle::DynamicBundle, world::World};
use std::{
collections::HashMap,
error::Error,
pin::Pin,
sync::RwLock,
task::{Context, Poll, Waker},
};
type AssetFetchFuture =
Pin<Box<dyn Future<Output = Result<DynamicBundle, Box<dyn Error>>> + Send + Sync>>;
pub struct FutureAssetFetch {
future_spawner: Box<dyn Fn(AssetPathStatic) -> AssetFetchFuture + Send + Sync>,
futures: RwLock<HashMap<AssetPathStatic, Option<AssetFetchFuture>>>,
}
impl FutureAssetFetch {
pub fn new<Fut>(future_spawner: impl Fn(AssetPathStatic) -> Fut + Send + Sync + 'static) -> Self
where
Fut: Future<Output = Result<DynamicBundle, Box<dyn Error>>> + Send + Sync + 'static,
{
Self {
future_spawner: Box::new(move |path| Box::pin(future_spawner(path))),
futures: Default::default(),
}
}
}
impl AssetFetch for FutureAssetFetch {
fn load_bytes(&self, path: AssetPath) -> Result<DynamicBundle, Box<dyn Error>> {
let path: AssetPathStatic = path.into_static();
self.futures
.write()
.map_err(|error| format!("{error}"))?
.insert(path.clone(), Some((self.future_spawner)(path)));
let mut bundle = DynamicBundle::default();
let _ = bundle.add_component(AssetAwaitsAsyncFetch);
Ok(bundle)
}
fn maintain(&mut self, storage: &mut World) -> Result<(), Box<dyn Error>> {
let mut cx = Context::from_waker(Waker::noop());
let mut futures = self.futures.write().map_err(|error| format!("{error}"))?;
for (path, future) in futures.iter_mut() {
if let Some(mut f) = future.take() {
match f.as_mut().poll(&mut cx) {
Poll::Ready(Ok(result)) => {
if let Some(entity) = storage.find_by::<true, _>(path) {
storage.remove::<(AssetAwaitsAsyncFetch,)>(entity)?;
storage.insert(entity, result)?;
}
}
Poll::Ready(Err(e)) => {
return Err(e);
}
Poll::Pending => {
*future = Some(f);
}
}
}
}
futures.retain(|_, v| v.is_some());
Ok(())
}
}