1use std::{
2 any::{type_name, Any, TypeId},
3 collections::HashMap,
4 sync::{Arc, RwLock, Weak},
5};
6
7#[derive(Clone, Default)]
9pub struct Resources {
10 values: Arc<RwLock<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>>,
11}
12
13#[derive(Clone)]
14pub struct WeakResources {
15 values: Weak<RwLock<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>>,
16}
17
18impl Resources {
19 pub fn new() -> Self {
20 Self::default()
21 }
22
23 pub fn downgrade(&self) -> WeakResources {
24 WeakResources {
25 values: Arc::downgrade(&self.values),
26 }
27 }
28
29 pub fn provide<T>(&self, value: T)
30 where
31 T: Send + Sync + 'static,
32 {
33 self.values
34 .write()
35 .expect("application resources poisoned")
36 .insert(TypeId::of::<T>(), Arc::new(value));
37 }
38
39 pub fn get<T>(&self) -> Option<Arc<T>>
40 where
41 T: Send + Sync + 'static,
42 {
43 self.values
44 .read()
45 .expect("application resources poisoned")
46 .get(&TypeId::of::<T>())
47 .cloned()
48 .and_then(|value| value.downcast::<T>().ok())
49 }
50
51 pub fn get_or_insert_with<T>(&self, create: impl FnOnce() -> T) -> Arc<T>
52 where
53 T: Send + Sync + 'static,
54 {
55 let mut values = self.values.write().expect("application resources poisoned");
56 values
57 .entry(TypeId::of::<T>())
58 .or_insert_with(|| Arc::new(create()))
59 .clone()
60 .downcast::<T>()
61 .unwrap_or_else(|_| {
62 panic!(
63 "application resource type mismatch for `{}`",
64 type_name::<T>()
65 )
66 })
67 }
68
69 pub fn require<T>(&self) -> Arc<T>
70 where
71 T: Send + Sync + 'static,
72 {
73 self.get::<T>()
74 .unwrap_or_else(|| panic!("missing application resource `{}`", type_name::<T>()))
75 }
76}
77
78impl WeakResources {
79 pub fn upgrade(&self) -> Option<Resources> {
80 self.values.upgrade().map(|values| Resources { values })
81 }
82}
83
84#[cfg(test)]
85#[path = "resources_test.rs"]
86mod tests;