minimo/
context.rs

1    use std::any::{Any, TypeId};
2    use std::collections::HashMap;
3    use std::sync::{Arc, RwLock};
4    use super::*;
5    pub struct Context {
6        data: Arc<RwLock<HashMap<TypeId, Box<dyn Any + Send + Sync >>>>,
7        named_data: Arc<RwLock<HashMap<(TypeId, String), Box<dyn Any + Send + Sync>>>>,
8    }
9
10    impl Context {
11        pub fn new() -> Self {
12            Self {
13                data: Arc::new(RwLock::new(HashMap::new())),
14                named_data: Arc::new(RwLock::new(HashMap::new())),
15            }
16        }
17
18        pub fn set<T: 'static + Clone + Send + Sync>(&self, value: T) {
19            let mut data = self.data.write().expect("Failed to acquire write lock");
20            data.insert(TypeId::of::<T>(), Box::new(value));
21        }
22
23        pub fn set_named<T: 'static + Clone + Send + Sync>(&self, name: &str, value: T) {
24            let mut data = self.named_data.write().expect("Failed to acquire write lock");
25            let key = (TypeId::of::<T>(), name.to_string());
26            data.insert(key, Box::new(value));
27        }
28
29        pub fn get<T: 'static + Clone + Send + Sync>(&self) -> Option<T> {
30            let data = self.data.read().expect("Failed to acquire read lock");
31            data.get(&TypeId::of::<T>())
32                .and_then(|boxed| boxed.downcast_ref::<T>())
33                .cloned()
34        }
35
36        pub fn get_named<T: 'static + Clone + Send + Sync>(&self, name: &str) -> Option<T> {
37            let data = self.named_data.read().expect("Failed to acquire read lock");
38            let key = (TypeId::of::<T>(), name.to_string());
39            data.get(&key)
40                .and_then(|boxed| boxed.downcast_ref::<T>())
41                .cloned()
42        }
43
44        pub fn update<T, F>(&self, f: F) -> crate::result::Result<()>
45        where
46            T: 'static + Clone + Send + Sync,
47            F: FnOnce(&mut T),
48        {
49            let mut data = self.data.write().expect("Failed to acquire write lock");
50            
51            if let Some(value) = data.get_mut(&TypeId::of::<T>()) {
52                if let Some(value) = value.downcast_mut::<T>() {
53                    f(value);
54                    Ok(())
55                } else {
56                    Err(format!("Type mismatch for {:?}", std::any::type_name::<T>()).into())
57                }
58            } else {
59                Err(format!("Type not found: {:?}", std::any::type_name::<T>()).into())
60            }
61        }
62
63        pub fn update_named<T, F>(&self, name: &str, f: F) -> crate::result::Result<()>
64        where
65            T: 'static + Clone + Send + Sync,
66            F: FnOnce(&mut T),
67        {
68            let mut data = self.named_data.write().expect("Failed to acquire write lock");
69            let key = (TypeId::of::<T>(), name.to_string());
70            
71            if let Some(value) = data.get_mut(&key) {
72                if let Some(value) = value.downcast_mut::<T>() {
73                    f(value);
74                    Ok(())
75                } else {
76                    Err(format!("Type mismatch for {:?}", std::any::type_name::<T>()).into())
77                }
78            } else {
79                Err(format!("Type not found: {:?}", std::any::type_name::<T>()).into())
80            }
81        }
82
83        pub fn remove<T: 'static>(&self) -> Option<Box<dyn Any + Send + Sync>> {
84            let mut data = self.data.write().expect("Failed to acquire write lock");
85            data.remove(&TypeId::of::<T>())
86        }
87
88        pub fn remove_named<T: 'static>(&self, name: &str) -> Option<Box<dyn Any + Send + Sync>> {
89            let mut data = self.named_data.write().expect("Failed to acquire write lock");
90            let key = (TypeId::of::<T>(), name.to_string());
91            data.remove(&key)
92        }
93
94        pub fn clear(&self) {
95            let mut data = self.data.write().expect("Failed to acquire write lock");
96            data.clear();
97            let mut named_data = self.named_data.write().expect("Failed to acquire write lock");
98            named_data.clear();
99        }
100
101        pub fn list(&self) {
102            showln!(yellow_bold, "context");
103            let data = self.data.read().expect("Failed to acquire read lock");
104            showln!(cyan_bold,"unnamed");
105            for (key, data) in data.iter() {
106 
107                let data_type = std::any::type_name_of_val(&data);
108                showln!(gray_dim, format!("{:?}", key), yellow_bold, " → ", white_bold, format!("{:?}", data_type))  ;
109            }
110            let named_data = self.named_data.read().expect("Failed to acquire read lock");
111            showln!(cyan_bold,"named");
112            for (key, data) in named_data.iter() {
113                showln!(gray_dim, format!("{:?}", key), yellow_bold, " → ", white_bold, format!("{:?}", data));
114            }
115        }
116    }
117
118    use std::sync::OnceLock;
119
120    use crate::{cyan_bold, showln};
121
122    pub fn global_context() -> &'static Context {
123        static INSTANCE: OnceLock<Context> = OnceLock::new();
124        INSTANCE.get_or_init(Context::new)
125    }
126
127    #[macro_export]
128    macro_rules! set {
129        ($value:expr) => {
130            $crate::context::global_context().set($value)
131        };
132        ($name:ident => $value:expr) => {
133            $crate::context::global_context().set_named(stringify!($name), $value)
134        };
135    }
136
137    #[macro_export]
138    macro_rules! get {
139        ($type:ty) => {
140            $crate::context::global_context().get::<$type>().unwrap()
141        };
142        ($type:ty, $name:ident) => {
143            $crate::context::global_context().get_named::<$type>(stringify!($name)).unwrap()
144        };
145    }
146
147    #[macro_export]
148    macro_rules! maybe {
149        ($type:ty) => {
150            $crate::context::global_context().get::<$type>()
151        };
152        ($type:ty, $name:ident) => {
153            $crate::context::global_context().get_named::<$type>(stringify!($name))
154        };
155    }
156
157    #[macro_export]
158    macro_rules! update {
159        ($type:ty, $($field:ident : $value:expr),+ $(,)?) => {
160            $crate::context::global_context().update::<$type, _>(|value| {
161                $(value.$field = $value;)+
162            })
163        };
164        ($type:ty, $name:ident, $($field:ident : $value:expr),+ $(,)?) => {
165            $crate::context::global_context().update_named::<$type, _>(stringify!($name), |value| {
166                $(value.$field = $value;)+
167            })
168        };
169        ($type:ty, |$param:ident| $body:expr) => {
170            $crate::context::global_context().update::<$type, _>(|$param| $body)
171        };
172        ($type:ty, $name:ident, |$param:ident| $body:expr) => {
173            $crate::context::global_context().update_named::<$type, _>(stringify!($name), |$param| $body)
174        };
175    }
176
177    #[macro_export]
178    macro_rules! remove {
179        ($type:ty) => {
180            $crate::context::global_context().remove::<$type>()
181        };
182        ($type:ty, $name:ident) => {
183            $crate::context::global_context().remove_named::<$type>(stringify!($name))
184        };
185    }
186
187    #[macro_export]
188    macro_rules! get_or {
189        ($type:ty, $default:expr) => {
190            $crate::context::global_context().get::<$type>().unwrap_or_else(|| $default)
191        };
192        ($type:ty, $name:ident, $default:expr) => {
193            $crate::context::global_context().get_named::<$type>(stringify!($name)).unwrap_or_else(|| $default)
194        };
195    }
196
197    #[macro_export]
198    macro_rules! get_or_else {
199        ($type:ty, $default:expr) => {
200            $crate::context::global_context().get::<$type>().unwrap_or_else($default)
201        };
202        ($type:ty, $name:ident, $default:expr) => {
203            $crate::context::global_context().get_named::<$type>(stringify!($name)).unwrap_or_else($default)
204        };
205    }
206
207    #[macro_export]
208    macro_rules! get_or_insert {
209        ($type:ty, $default:expr) => {{
210            if let Some(value) = $crate::context::global_context().get::<$type>() {
211                value
212            } else {
213                let default = $default;
214                $crate::context::global_context().set(default.clone());
215                default
216            }
217        }};
218        ($type:ty, $name:ident, $default:expr) => {{
219            if let Some(value) = $crate::context::global_context().get_named::<$type>(stringify!($name)) {
220                value
221            } else {
222                let default = $default;
223                $crate::context::global_context().set_named(stringify!($name), default.clone());
224                default
225            }
226        }};
227    }
228
229    #[cfg(test)]
230    mod tests {
231        use super::*;
232
233        #[derive(Debug, Clone, PartialEq)]
234        struct User {
235            name: String,
236            age: u32,
237        }
238
239        #[test]
240        fn test_context() {
241            // Set and get a simple value
242            set!(42i32);
243            assert_eq!(get!(i32), 42);
244            assert_eq!(maybe!(i32), Some(42));
245
246            // Set and get a named value
247            set!(name => "John".to_string());
248            assert_eq!(get!(String, name), "John".to_string());
249            assert_eq!(maybe!(String, name), Some("John".to_string()));
250
251            // Set and get a complex value
252            let user = User {
253                name: "Alice".to_string(),
254                age: 30,
255            };
256            set!(user.clone());
257            assert_eq!(get!(User), user);
258
259            // Test getting a specific field using a closure
260            assert_eq!(get!(User).name, "Alice");
261            assert_eq!(maybe!(User).map(|u| u.age), Some(30));
262
263            // Test updating a value using the update! macro
264            update!(User, name: "Bob".to_string()).unwrap();
265            update!(User, |u| u.age += 1).unwrap();
266
267            assert_eq!(
268                get!(User),
269                User {
270                    name: "Bob".to_string(),
271                    age: 31
272                }
273            );
274
275            // Test named updates
276            set!(named_user => User { name: "Charlie".to_string(), age: 25 });
277            update!(User, named_user, age: 26).unwrap();
278            assert_eq!(get!(User, named_user).age, 26);
279
280            // Test remove
281            remove!(User);
282            assert_eq!(maybe!(User), None);
283
284            // Test convenience macros
285            assert_eq!(get_or!(i32, 0), 42);
286            assert_eq!(get_or!(Vec<i32>, vec![1, 2, 3]), vec![1, 2, 3]);
287            
288            assert_eq!(get_or_else!(i32, || 0), 42);
289            assert_eq!(get_or_else!(Vec<i32>, || vec![4, 5, 6]), vec![4, 5, 6]);
290
291            let inserted: Vec<i32> = get_or_insert!(Vec<i32>, vec![7, 8, 9]);
292            assert_eq!(inserted, vec![7, 8, 9]);
293            assert_eq!(get!(Vec<i32>), vec![7, 8, 9]);
294
295            // Test named get_or, get_or_else, and get_or_insert
296            assert_eq!(get_or!(String, other_name, "Default".to_string()), "Default".to_string());
297            assert_eq!(get_or_else!(String, other_name, || "Default".to_string()), "Default".to_string());
298            let inserted_named: String = get_or_insert!(String, new_name, "New".to_string());
299            assert_eq!(inserted_named, "New");
300            assert_eq!(get!(String, new_name), "New");
301
302            // Test clear
303            global_context().clear();
304            assert_eq!(maybe!(i32), None);
305            assert_eq!(maybe!(User), None);
306            assert_eq!(maybe!(Vec<i32>), None);
307            assert_eq!(maybe!(String, name), None);
308        }
309    }