mail_core/resource/
loading.rs

1//! This modules add some helpers to load all resources in some data type.
2use std::{
3    borrow::{ToOwned, Borrow},
4    collections::HashMap,
5    mem
6};
7
8use futures::{
9    Future, Poll, Async,
10    try_ready,
11    future::{self, JoinAll}
12};
13
14use crate::{
15    Context, MaybeEncData, Resource,
16    utils::SendBoxFuture,
17    error::ResourceLoadingError
18};
19
20pub trait ContainedResourcesAccess {
21    type Key: ToOwned + ?Sized;
22
23    /// Visit all resources.
24    ///
25    /// This method is not allowed to be implemented in a way that
26    /// it might visit resources, which are not accessable with
27    /// [`Self.access_resource_mut()`]. This means that without
28    /// e.g. a `RwLock` this can not visit resources in a `Arc`.
29    fn visit_resources(&self, visitor: &mut impl FnMut(&Self::Key, &Resource));
30
31    /// Return a mut ref for a resource based on given key.
32    ///
33    /// If a resource is visited in a `Self.visit_resources()` call
34    /// and the state of self was not changes this method has to
35    /// be able to return a mut reference to it.
36    ///
37    /// To allow accessing resources in a mutext this does pass
38    /// the mut ref to a closure instead of returning it.
39    ///
40    fn access_resource_mut<R>(
41        &mut self,
42        key: &Self::Key,
43        modify: impl FnOnce(Option<&mut Resource>) -> R
44    ) -> R;
45
46    /// Return a ref for a resource base on given key.
47    fn access_resource<R>(
48        &self,
49        key: &Self::Key,
50        modify: impl FnOnce(Option<&Resource>) -> R
51    ) -> R;
52}
53
54
55impl ContainedResourcesAccess for Vec<Resource> {
56    type Key = usize;
57
58    fn visit_resources(&self, visitor: &mut impl FnMut(&Self::Key, &Resource)) {
59        for (idx, resource) in self.iter().enumerate() {
60            visitor(&idx, resource)
61        }
62    }
63
64    fn access_resource_mut<R>(
65        &mut self,
66        key: &Self::Key,
67        modify: impl FnOnce(Option<&mut Resource>) -> R
68    ) -> R {
69        modify(self.get_mut(*key))
70    }
71
72    /// Return a ref for a resource base on given key.
73    fn access_resource<R>(
74        &self,
75        key: &Self::Key,
76        modify: impl FnOnce(Option<&Resource>) -> R
77    ) -> R {
78        modify(self.get(*key))
79    }
80}
81
82impl ContainedResourcesAccess for HashMap<String, Resource> {
83    type Key = str;
84
85    fn visit_resources(&self, visitor: &mut impl FnMut(&Self::Key, &Resource)) {
86        for (key, resource) in self.iter() {
87            visitor(&key, resource)
88        }
89    }
90
91    fn access_resource_mut<R>(
92        &mut self,
93        key: &Self::Key,
94        modify: impl FnOnce(Option<&mut Resource>) -> R
95    ) -> R {
96        modify(self.get_mut(key))
97    }
98
99    /// Return a ref for a resource base on given key.
100    fn access_resource<R>(
101        &self,
102        key: &Self::Key,
103        modify: impl FnOnce(Option<&Resource>) -> R
104    ) -> R {
105        modify(self.get(key))
106    }
107}
108
109//TODO[feat] impl. where applicable in std (Box, BTreeMap, other HashMap, etc.)
110
111
112impl Resource {
113
114    pub fn load_container<CO>(container: CO, ctx: &impl Context)
115        -> ResourceContainerLoadingFuture<CO>
116        where CO: ContainedResourcesAccess
117    {
118        ResourceContainerLoadingFuture::start_loading(container, ctx)
119    }
120}
121
122pub struct ResourceContainerLoadingFuture<C>
123    where C: ContainedResourcesAccess
124{
125    inner: Option<InnerFuture<C>>
126}
127
128struct InnerFuture<C>
129    where C: ContainedResourcesAccess
130{
131    container: C,
132    keys: Vec<<C::Key as ToOwned>::Owned>,
133    futs: JoinAll<Vec<SendBoxFuture<MaybeEncData, ResourceLoadingError>>>
134}
135
136impl<CO> ResourceContainerLoadingFuture<CO>
137    where CO: ContainedResourcesAccess
138{
139    pub fn start_loading(container: CO, ctx: &impl Context) -> Self {
140        let mut keys = Vec::new();
141        let mut futs = Vec::new();
142
143        container.visit_resources(&mut |key, resource| {
144            if let &Resource::Source(ref source) = resource {
145                let fut = ctx.load_resource(source);
146                futs.push(fut);
147                keys.push(key.to_owned());
148            }
149        });
150
151        let futs = future::join_all(futs);
152
153        ResourceContainerLoadingFuture {
154            inner: Some(InnerFuture {
155                container,
156                keys,
157                futs
158            }),
159        }
160    }
161}
162
163impl<C> Future for ResourceContainerLoadingFuture<C>
164    where C: ContainedResourcesAccess
165{
166    type Item = C;
167    type Error = ResourceLoadingError;
168
169    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
170        let loaded;
171
172        if let Some(loading) = self.inner.as_mut().map(|inner| &mut inner.futs) {
173            loaded = try_ready!(loading.poll());
174        } else {
175            panic!("future called after it resolved");
176        };
177
178        //UNWRAP_SAFE: can only be reached if it was some
179        let InnerFuture { mut container, keys, futs:_ } = self.inner.take().unwrap();
180
181        for (key, new_resource) in keys.into_iter().zip(loaded.into_iter()) {
182            container.access_resource_mut(key.borrow(), |resource_ref| {
183                if let Some(resource_ref) = resource_ref {
184                    mem::replace(resource_ref, new_resource.to_resource());
185                }
186            })
187        }
188
189        Ok(Async::Ready(container))
190    }
191}