rafx_base/
resource_map.rs

1//! Allows placing resources (i.e. "global" data) in a dictionary and looking it up by type. The data
2//! could be "global" systems, component storages, component factories, etc.
3//!
4//! This implements a type system for expressing read/write dependencies. Many readers and single
5//! writers are allowed, but not both at the same time. This is checked at runtime, not compile time.
6//!
7//! Lots of inspiration taken from `shred` for how to create a type system
8//! to express read/write dependencies
9
10//
11// ResourceId
12//
13use std::any::TypeId;
14use std::marker::PhantomData;
15use std::prelude::v1::*;
16
17use downcast_rs::Downcast;
18use fnv::FnvHashMap as HashMap;
19
20use crate::trust_cell::{Ref, RefMut, TrustCell};
21
22/// Every type can be converted to a `ResourceId`. The ResourceId is used to look up the type's value
23/// in the `ResourceMap`
24#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
25pub struct ResourceId {
26    type_id: TypeId,
27}
28
29impl ResourceId {
30    /// Creates a new resource id from a given type.
31    #[inline]
32    pub fn new<T: 'static>() -> Self {
33        ResourceId {
34            type_id: std::any::TypeId::of::<T>(),
35        }
36    }
37}
38
39/// Any data that can be stored in the ResourceMap must implement this trait. There is a blanket
40/// implementation provided for all compatible types
41pub trait Resource: Downcast + Send + Sync + 'static {}
42
43impl<T> Resource for T where T: Downcast + Send + Sync {}
44
45// Used for downcastic
46mod __resource_mopafy_scope {
47    #![allow(clippy::all)]
48
49    use super::Resource;
50
51    downcast_rs::impl_downcast!(Resource);
52}
53
54/// Builder for creating a ResourceMap
55pub struct ResourceMapBuilder {
56    /// The ResourceMap being built
57    resource_map: ResourceMap,
58}
59
60impl ResourceMapBuilder {
61    /// Creates an empty builder
62    pub fn new() -> Self {
63        ResourceMapBuilder {
64            resource_map: ResourceMap::new(),
65        }
66    }
67
68    /// Builder-style API that adds the resource to the map
69    pub fn with_resource<R>(
70        mut self,
71        r: R,
72    ) -> Self
73    where
74        R: Resource,
75    {
76        self.resource_map.insert(r);
77        self
78    }
79
80    /// Adds the resource to the map
81    pub fn insert<R>(
82        &mut self,
83        r: R,
84    ) where
85        R: Resource,
86    {
87        self.resource_map.insert(r);
88    }
89
90    /// Consume this builder, returning the resource map
91    pub fn build(self) -> ResourceMap {
92        self.resource_map
93    }
94}
95
96/// A key-value structure. The key is a type, and the value is a single object of that type
97#[derive(Default)]
98pub struct ResourceMap {
99    resources: HashMap<ResourceId, TrustCell<Box<dyn Resource>>>,
100}
101
102impl ResourceMap {
103    /// Creates an empty resource map
104    pub fn new() -> Self {
105        ResourceMap {
106            resources: HashMap::default(),
107        }
108    }
109
110    pub fn try_insert_default<R>(&mut self)
111    where
112        R: Resource + Default,
113    {
114        if !self.has_value::<R>() {
115            self.insert(R::default());
116        }
117    }
118
119    /// Add a type/resource instance to the map
120    pub fn insert<R>(
121        &mut self,
122        r: R,
123    ) where
124        R: Resource,
125    {
126        self.insert_by_id(ResourceId::new::<R>(), r);
127    }
128
129    /// Remove a type/resource instance from the map
130    pub fn remove<R>(&mut self) -> Option<R>
131    where
132        R: Resource,
133    {
134        self.remove_by_id(ResourceId::new::<R>())
135    }
136
137    fn insert_by_id<R>(
138        &mut self,
139        id: ResourceId,
140        r: R,
141    ) where
142        R: Resource,
143    {
144        //TODO: Do not allow silent overwrite
145        let _old = self.resources.insert(id, TrustCell::new(Box::new(r)));
146        //assert!(old.is_none());
147    }
148
149    fn remove_by_id<R>(
150        &mut self,
151        id: ResourceId,
152    ) -> Option<R>
153    where
154        R: Resource,
155    {
156        self.resources
157            .remove(&id)
158            .map(TrustCell::into_inner)
159            .map(|x: Box<dyn Resource>| x.downcast())
160            .map(|x: Result<Box<R>, _>| x.ok().unwrap())
161            .map(|x| *x)
162    }
163
164    fn unwrap_resource<R>(resource: Option<R>) -> R {
165        if resource.is_none() {
166            let name = core::any::type_name::<R>();
167            // Tried to fetch or fetch_mut on a resource that is not registered.
168            panic!("Resource not found: {}", name);
169        }
170
171        resource.unwrap()
172    }
173
174    /// Read-only fetch of a resource. Trying to get a resource that is not in the map is fatal. Use
175    /// try_fetch if unsure whether the resource exists. Requesting read access to a resource that
176    /// has any concurrently active writer is fatal.
177    pub fn fetch<R: Resource>(&self) -> ReadBorrow<R> {
178        let result = self.try_fetch();
179        Self::unwrap_resource(result)
180    }
181
182    /// Read-only fetch of a resource. Requesting read access to a resource that has a concurrently
183    /// active writer is fatal. Returns None if the type is not registered.
184    pub fn try_fetch<R: Resource>(&self) -> Option<ReadBorrow<R>> {
185        let res_id = ResourceId::new::<R>();
186
187        self.resources.get(&res_id).map(|r| ReadBorrow {
188            inner: Ref::map(r.borrow(), Box::as_ref),
189            phantom: PhantomData,
190        })
191    }
192
193    /// Read/Write fetch of a resource. Trying to get a resource that is not in the map is fatal. Use
194    /// try_fetch if unsure whether the resource exists. Requesting write access to a resource with
195    /// any concurrently active read/write is fatal
196    pub fn fetch_mut<R: Resource>(&self) -> WriteBorrow<R> {
197        let result = self.try_fetch_mut();
198        Self::unwrap_resource(result)
199    }
200
201    /// Read/Write fetch of a resource. Requesting write access to a resource with
202    /// any concurrently active read/write is fatal. Returns None if the type is not registered.
203    pub fn try_fetch_mut<R: Resource>(&self) -> Option<WriteBorrow<R>> {
204        let res_id = ResourceId::new::<R>();
205
206        self.resources.get(&res_id).map(|r| WriteBorrow::<R> {
207            inner: RefMut::map(r.borrow_mut(), Box::as_mut),
208            phantom: PhantomData,
209        })
210    }
211
212    /// Returns true if the resource is registered.
213    pub fn has_value<R>(&self) -> bool
214    where
215        R: Resource,
216    {
217        self.has_value_raw(ResourceId::new::<R>())
218    }
219
220    fn has_value_raw(
221        &self,
222        id: ResourceId,
223    ) -> bool {
224        self.resources.contains_key(&id)
225    }
226
227    /// Iterate all ResourceIds within the dictionary
228    pub fn keys(&self) -> impl Iterator<Item = &ResourceId> {
229        self.resources.iter().map(|x| x.0)
230    }
231}
232
233/// DataRequirement base trait, which underlies Read<T> and Write<T> requests
234pub trait DataRequirement<'a> {
235    type Borrow: DataBorrow;
236
237    fn fetch(resource_map: &'a ResourceMap) -> Self::Borrow;
238}
239
240// Implementation for () required because we build support for (), (A), (A, B), (A, B, ...) inductively
241impl<'a> DataRequirement<'a> for () {
242    type Borrow = ();
243
244    fn fetch(_: &'a ResourceMap) -> Self::Borrow {}
245}
246
247/// This type represents requesting read access to T. If T is not registered, trying to fill this
248/// request will be fatal
249pub struct Read<T: Resource> {
250    phantom_data: PhantomData<T>,
251}
252
253/// Same as `Read`, but will return None rather than being fatal
254pub type ReadOption<T> = Option<Read<T>>;
255
256impl<'a, T: Resource> DataRequirement<'a> for Read<T> {
257    type Borrow = ReadBorrow<'a, T>;
258
259    fn fetch(resource_map: &'a ResourceMap) -> Self::Borrow {
260        resource_map.fetch::<T>()
261    }
262}
263
264impl<'a, T: Resource> DataRequirement<'a> for Option<Read<T>> {
265    type Borrow = Option<ReadBorrow<'a, T>>;
266
267    fn fetch(resource_map: &'a ResourceMap) -> Self::Borrow {
268        resource_map.try_fetch::<T>()
269    }
270}
271
272/// This type represents requesting write access to T. If T is not registered, trying to fill this
273/// request will be fatal
274pub struct Write<T: Resource> {
275    phantom_data: PhantomData<T>,
276}
277
278/// Same as `Write`, but will return None rather than being fatal
279pub type WriteOption<T> = Option<Write<T>>;
280
281impl<'a, T: Resource> DataRequirement<'a> for Write<T> {
282    type Borrow = WriteBorrow<'a, T>;
283
284    fn fetch(resource_map: &'a ResourceMap) -> Self::Borrow {
285        resource_map.fetch_mut::<T>()
286    }
287}
288
289impl<'a, T: Resource> DataRequirement<'a> for Option<Write<T>> {
290    type Borrow = Option<WriteBorrow<'a, T>>;
291
292    fn fetch(resource_map: &'a ResourceMap) -> Self::Borrow {
293        resource_map.try_fetch_mut::<T>()
294    }
295}
296
297/// Borrow base trait. This base trait is required to allow inductively composing tuples of ReadBorrow/WriteBorrow
298/// i.e. (), (A), (A, B), (A, B, ...) inductively
299pub trait DataBorrow {}
300
301// Implementation for () required because we build support for (), (A), (A, B), (A, B, ...) inductively
302impl DataBorrow for () {}
303
304/// Represents a filled read-only request for T
305pub struct ReadBorrow<'a, T> {
306    inner: Ref<'a, dyn Resource>,
307    phantom: PhantomData<&'a T>,
308}
309
310impl<'a, T> DataBorrow for ReadBorrow<'a, T> {}
311impl<'a, T> DataBorrow for Option<ReadBorrow<'a, T>> {}
312
313impl<'a, T> std::ops::Deref for ReadBorrow<'a, T>
314where
315    T: Resource,
316{
317    type Target = T;
318
319    fn deref(&self) -> &T {
320        self.inner.downcast_ref().unwrap()
321    }
322}
323
324impl<'a, T> Clone for ReadBorrow<'a, T> {
325    fn clone(&self) -> Self {
326        ReadBorrow {
327            inner: self.inner.clone(),
328            phantom: PhantomData,
329        }
330    }
331}
332
333/// Represents a filled read/write request for T
334pub struct WriteBorrow<'a, T> {
335    inner: RefMut<'a, dyn Resource>,
336    phantom: PhantomData<&'a mut T>,
337}
338
339impl<'a, T> DataBorrow for WriteBorrow<'a, T> {}
340impl<'a, T> DataBorrow for Option<WriteBorrow<'a, T>> {}
341
342impl<'a, T> std::ops::Deref for WriteBorrow<'a, T>
343where
344    T: Resource,
345{
346    type Target = T;
347
348    fn deref(&self) -> &T {
349        self.inner.downcast_ref().unwrap()
350    }
351}
352
353impl<'a, T> std::ops::DerefMut for WriteBorrow<'a, T>
354where
355    T: Resource,
356{
357    fn deref_mut(&mut self) -> &mut T {
358        self.inner.downcast_mut().unwrap()
359    }
360}
361
362// This macro is used to inductively build tuples i.e. (), (A), (A, B), (A, B, ...) inductively
363macro_rules! impl_data {
364    ( $($ty:ident),* ) => {
365
366        //
367        // Make tuples containing DataBorrow types implement DataBorrow
368        //
369        impl<$($ty),*> DataBorrow for ( $( $ty , )* )
370        where $( $ty : DataBorrow ),*
371        {
372
373        }
374
375        //
376        // Make tuples containing DataRequirement types implement DataBorrow. Additionally implement
377        // fetch
378        //
379        impl<'a, $($ty),*> DataRequirement<'a> for ( $( $ty , )* )
380        where $( $ty : DataRequirement<'a> ),*
381        {
382            type Borrow = ( $( <$ty as DataRequirement<'a>>::Borrow, )* );
383
384            fn fetch(resource_map: &'a ResourceMap) -> Self::Borrow {
385                #![allow(unused_variables)]
386                ( $( <$ty as DataRequirement<'a>>::fetch(resource_map), )* )
387            }
388        }
389    };
390}
391
392mod impl_data {
393    #![cfg_attr(rustfmt, rustfmt_skip)]
394
395    use super::*;
396
397    // Build tuples for DataBorrow/DataRequirement i.e. (), (A), (A, B), (A, B, ...) inductively
398    impl_data!(A);
399    impl_data!(A, B);
400    impl_data!(A, B, C);
401    impl_data!(A, B, C, D);
402    impl_data!(A, B, C, D, E);
403    impl_data!(A, B, C, D, E, F);
404    impl_data!(A, B, C, D, E, F, G);
405    impl_data!(A, B, C, D, E, F, G, H);
406    impl_data!(A, B, C, D, E, F, G, H, I);
407    impl_data!(A, B, C, D, E, F, G, H, I, J);
408    // May be extended as needed, but this seems like enough
409    // impl_data!(A, B, C, D, E, F, G, H, I, J, K);
410    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L);
411    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M);
412    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
413    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
414    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
415    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
416    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
417    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
418    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);
419    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);
420    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V);
421    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W);
422    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X);
423    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y);
424    // impl_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);
425}