use std::{
borrow::{ToOwned, Borrow},
collections::HashMap,
mem
};
use futures::{
Future, Poll, Async,
try_ready,
future::{self, JoinAll}
};
use crate::{
Context, MaybeEncData, Resource,
utils::SendBoxFuture,
error::ResourceLoadingError
};
pub trait ContainedResourcesAccess {
type Key: ToOwned + ?Sized;
fn visit_resources(&self, visitor: &mut impl FnMut(&Self::Key, &Resource));
fn access_resource_mut<R>(
&mut self,
key: &Self::Key,
modify: impl FnOnce(Option<&mut Resource>) -> R
) -> R;
fn access_resource<R>(
&self,
key: &Self::Key,
modify: impl FnOnce(Option<&Resource>) -> R
) -> R;
}
impl ContainedResourcesAccess for Vec<Resource> {
type Key = usize;
fn visit_resources(&self, visitor: &mut impl FnMut(&Self::Key, &Resource)) {
for (idx, resource) in self.iter().enumerate() {
visitor(&idx, resource)
}
}
fn access_resource_mut<R>(
&mut self,
key: &Self::Key,
modify: impl FnOnce(Option<&mut Resource>) -> R
) -> R {
modify(self.get_mut(*key))
}
fn access_resource<R>(
&self,
key: &Self::Key,
modify: impl FnOnce(Option<&Resource>) -> R
) -> R {
modify(self.get(*key))
}
}
impl ContainedResourcesAccess for HashMap<String, Resource> {
type Key = str;
fn visit_resources(&self, visitor: &mut impl FnMut(&Self::Key, &Resource)) {
for (key, resource) in self.iter() {
visitor(&key, resource)
}
}
fn access_resource_mut<R>(
&mut self,
key: &Self::Key,
modify: impl FnOnce(Option<&mut Resource>) -> R
) -> R {
modify(self.get_mut(key))
}
fn access_resource<R>(
&self,
key: &Self::Key,
modify: impl FnOnce(Option<&Resource>) -> R
) -> R {
modify(self.get(key))
}
}
impl Resource {
pub fn load_container<CO>(container: CO, ctx: &impl Context)
-> ResourceContainerLoadingFuture<CO>
where CO: ContainedResourcesAccess
{
ResourceContainerLoadingFuture::start_loading(container, ctx)
}
}
pub struct ResourceContainerLoadingFuture<C>
where C: ContainedResourcesAccess
{
inner: Option<InnerFuture<C>>
}
struct InnerFuture<C>
where C: ContainedResourcesAccess
{
container: C,
keys: Vec<<C::Key as ToOwned>::Owned>,
futs: JoinAll<Vec<SendBoxFuture<MaybeEncData, ResourceLoadingError>>>
}
impl<CO> ResourceContainerLoadingFuture<CO>
where CO: ContainedResourcesAccess
{
pub fn start_loading(container: CO, ctx: &impl Context) -> Self {
let mut keys = Vec::new();
let mut futs = Vec::new();
container.visit_resources(&mut |key, resource| {
if let &Resource::Source(ref source) = resource {
let fut = ctx.load_resource(source);
futs.push(fut);
keys.push(key.to_owned());
}
});
let futs = future::join_all(futs);
ResourceContainerLoadingFuture {
inner: Some(InnerFuture {
container,
keys,
futs
}),
}
}
}
impl<C> Future for ResourceContainerLoadingFuture<C>
where C: ContainedResourcesAccess
{
type Item = C;
type Error = ResourceLoadingError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let loaded;
if let Some(loading) = self.inner.as_mut().map(|inner| &mut inner.futs) {
loaded = try_ready!(loading.poll());
} else {
panic!("future called after it resolved");
};
let InnerFuture { mut container, keys, futs:_ } = self.inner.take().unwrap();
for (key, new_resource) in keys.into_iter().zip(loaded.into_iter()) {
container.access_resource_mut(key.borrow(), |resource_ref| {
if let Some(resource_ref) = resource_ref {
mem::replace(resource_ref, new_resource.to_resource());
}
})
}
Ok(Async::Ready(container))
}
}