aboutsummaryrefslogtreecommitdiff
path: root/mingling_core/src/asset/global_resource.rs
blob: 5667576f02f4381ad007d186fa67ad40245420d8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use std::{
    any::{Any, TypeId},
    collections::HashMap,
    sync::{Arc, Mutex},
};

use crate::{ChainProcess, Program, ProgramCollect};

pub type GlobalResources = Arc<Mutex<HashMap<TypeId, Box<dyn Any + Sync + Send>>>>;

impl<C> Program<C>
where
    C: ProgramCollect<Enum = C>,
{
    /// Insert a resource of the given type, cloning the provided value into the store
    pub fn with_resource<Res: 'static + Send + Sync + ResourceMarker>(&mut self, res: Res) {
        if let Ok(mut guard) = self.resources.lock() {
            guard.insert(TypeId::of::<Res>(), Box::new(Arc::new(res)));
        }
    }

    /// Modify a resource by type, applying a closure to the resource if present
    pub fn modify_res<Res>(&self, f: impl FnOnce(&mut Res))
    where
        Res: 'static + Default + ResourceMarker + Send + Sync,
    {
        let mut guard = match self.resources.lock() {
            Ok(guard) => guard,
            Err(_) => {
                return;
            }
        };
        if let Some(arc_res) = guard
            .get_mut(&TypeId::of::<Res>())
            .and_then(|a| a.downcast_mut::<Arc<Res>>())
        {
            let mut new_res = match Arc::try_unwrap(std::mem::take(arc_res)) {
                Ok(val) => val,
                Err(arc) => (*arc).res_clone(),
            };
            f(&mut new_res);
            *arc_res = Arc::new(new_res);
        }
    }

    /// Get an resources by type, returning `Res` if present
    pub fn res<Res: 'static + Send + Sync>(&self) -> Option<GlobalResource<Res>> {
        let guard = self.resources.lock().ok()?;
        let boxed_any = guard.get(&TypeId::of::<Res>())?;
        let arc_res = boxed_any.as_ref().downcast_ref::<Arc<Res>>()?;
        Some(GlobalResource::from(Arc::clone(arc_res)))
    }

    /// Get a resource by type, returning `GlobalResource<Res>` if present
    pub fn res_or_route<Res: 'static + Send + Sync>(
        &self,
        route: ChainProcess<C>,
    ) -> Result<GlobalResource<Res>, ChainProcess<C>> {
        match self.res() {
            Some(r) => Ok(r),
            None => Err(route),
        }
    }

    /// Get a resource by type, returning `GlobalResource<Res>` or inserting a default
    pub fn res_or_default<Res: 'static + Send + Sync + ResourceMarker>(
        &self,
    ) -> GlobalResource<Res> {
        self.res()
            .unwrap_or_else(|| GlobalResource::from(Arc::new(Res::res_default())))
    }
}

/// Global assets for storing Program global state information
pub struct GlobalResource<ResType: 'static + Send + Sync> {
    res_arc: Arc<ResType>,
}

impl<ResType: 'static + Send + Sync> GlobalResource<ResType> {
    /// Create a new `GlobalAsset` from an `AssetType` value.
    pub fn new(res: ResType) -> Self {
        Self {
            res_arc: Arc::new(res),
        }
    }
}

impl<ResType: 'static + Send + Sync> From<Arc<ResType>> for GlobalResource<ResType> {
    fn from(arc: Arc<ResType>) -> Self {
        Self { res_arc: arc }
    }
}

impl<ResType: 'static + Send + Sync> std::ops::Deref for GlobalResource<ResType> {
    type Target = ResType;

    fn deref(&self) -> &Self::Target {
        &self.res_arc
    }
}

/// Resource marker trait, types that implement the Clone and Default traits can be considered as resources
pub trait ResourceMarker {
    fn res_clone(&self) -> Self;
    fn res_default() -> Self;
}

impl<T: Default + Clone> ResourceMarker for T {
    fn res_clone(&self) -> Self {
        Clone::clone(self)
    }

    fn res_default() -> Self {
        Default::default()
    }
}