gom/
lib.rs

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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use std::{
    any::{Any, TypeId},
    collections::HashMap,
    sync::Mutex,
};

use lazy_static::lazy_static;

lazy_static! {
    static ref REGISTRY: Mutex<HashMap<TypeId, Box<dyn Any + Send>>> = Mutex::new(HashMap::new());
}

/// Global Object Registry Visitor
///
/// This struct is used to register and access global objects of a specific type.
///
/// # Example
/// ```rust
/// use gom::Registry;
///
/// Registry::register("key", 123);
/// let value = Registry::<i32>::apply("key", |v| *v + 1);
/// assert_eq!(value, Some(124));
/// ```
pub struct Registry<T> {
    _use: Option<T>,
}

impl<T: Send + 'static> Registry<T> {
    /// Register a new value with the given key
    ///
    /// If the key already exists in the same type, the value will be overwritten.
    ///
    /// # Example
    /// ```rust
    /// use gom::Registry;
    ///
    /// Registry::register("key", 123);
    /// ```
    pub fn register(key: &str, value: T) {
        let mut registry = REGISTRY.lock().unwrap();
        // Check if the map already exists for this type
        if !registry.contains_key(&TypeId::of::<T>()) {
            let map: HashMap<String, T> = HashMap::new();
            registry.insert(TypeId::of::<T>(), Box::new(map));
        }
        // Get type map
        let map = registry
            .get_mut(&TypeId::of::<T>())
            .unwrap()
            .downcast_mut::<HashMap<String, T>>()
            .unwrap();
        // Insert the value into the map
        map.insert(key.to_string(), value);
    }

    /// Apply a function to the value with the given key
    ///
    /// **If this function is nested, it will cause thread deadlock.**
    ///
    /// If the key does not exist, the function will not be called and `None` will be returned.
    ///
    /// # Returns
    /// `Some(R)` if the key exists and the function was applied successfully, `None` otherwise.
    ///
    /// # Example
    /// ```rust
    /// use gom::Registry;
    ///
    /// Registry::register("key", 123);
    /// let value = Registry::<i32>::apply("key", |v| *v + 1);
    /// assert_eq!(value, Some(124));
    /// ```
    pub fn apply<R, F: FnOnce(&mut T) -> R>(key: &str, f: F) -> Option<R> {
        let mut registry = REGISTRY.lock().unwrap();
        // Get type map
        let map = registry
            .get_mut(&TypeId::of::<T>())?
            .downcast_mut::<HashMap<String, T>>()?;
        // Get value
        let value = map.get_mut(key)?;
        Some(f(value))
    }

    /// Get the value with the given key and reomve it from the registry
    /// 
    /// If the key does not exist, `None` will be returned.
    ///
    /// # Returns
    /// `Some(T)` if the key exists, `None` otherwise.
    ///
    /// # Example
    /// ```rust
    /// use gom::Registry;
    ///
    /// Registry::register("key", 123);
    /// let value = Registry::<i32>::remove("key");
    /// assert_eq!(value, Some(123));
    /// let value = Registry::<i32>::remove("key");
    /// assert_eq!(value, None);
    /// ```
    pub fn remove(key: &str) -> Option<T> {
        let mut registry = REGISTRY.lock().unwrap();
        // Get type map
        let map = registry
            .get_mut(&TypeId::of::<T>())?
            .downcast_mut::<HashMap<String, T>>()?;
        // Get value
        map.remove(key)
    }

    /// Apply a function to the value with the given key
    /// 
    /// **This function will not cause thread deadlocks.**
    /// 
    /// If the key does not exist, the function will not be called and `None` will be returned.
    /// 
    /// In the context of 'with', the value cannot be retrieved again.
    ///
    /// # Returns
    /// `Some(R)` if the key exists and the function was applied successfully, `None` otherwise.
    ///
    /// # Example
    /// ```rust
    /// use gom::Registry;
    ///
    /// Registry::register("key", 123);
    /// let value = Registry::<i32>::with("key", |v| *v + 1);
    /// assert_eq!(value, Some(124));
    /// ```
    pub fn with<R, F: FnOnce(&mut T) -> R>(key: &str, f: F) -> Option<R> {
        let mut value = Self::remove(key)?;
        let result = f(&mut value);
        Self::register(key, value);
        Some(result)
    }
}

/// Make a identifier string with the given path
///
/// ```rust
/// use gom::id;
///
/// const MY_ID: &str = id!(my.module.MyType);
/// const OTHER_ID: &str = id!(@MY_ID.other.OtherType);
///
/// assert_eq!(MY_ID, ".my.module.MyType");
/// assert_eq!(OTHER_ID, ".my.module.MyType.other.OtherType");
/// ```
#[macro_export]
macro_rules! id {
    ($($x:ident).+) => {
        concat!($('.', stringify!($x)),+)
    };
    (@ $root:ident . $($x:ident).+) => {
        {
            const ROOT: &str = $root;
            const PATH: &str = concat!($('.', stringify!($x)),+);
            constcat::concat!(ROOT, PATH)
        }
    }
}