Skip to main content

react_rs_core/
context.rs

1use std::any::{Any, TypeId};
2use std::cell::RefCell;
3use std::collections::HashMap;
4
5thread_local! {
6    static CONTEXT_MAP: RefCell<HashMap<TypeId, Box<dyn Any>>> = RefCell::new(HashMap::new());
7}
8
9pub fn provide_context<T: Clone + 'static>(value: T) {
10    CONTEXT_MAP.with(|map| {
11        map.borrow_mut().insert(TypeId::of::<T>(), Box::new(value));
12    });
13}
14
15pub fn use_context<T: Clone + 'static>() -> Option<T> {
16    CONTEXT_MAP.with(|map| {
17        map.borrow()
18            .get(&TypeId::of::<T>())
19            .and_then(|v| v.downcast_ref::<T>())
20            .cloned()
21    })
22}
23
24pub fn use_context_or<T: Clone + 'static>(default: T) -> T {
25    use_context::<T>().unwrap_or(default)
26}
27
28pub fn clear_context<T: 'static>() {
29    CONTEXT_MAP.with(|map| {
30        map.borrow_mut().remove(&TypeId::of::<T>());
31    });
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    #[derive(Clone, Debug, PartialEq)]
39    struct Theme {
40        dark_mode: bool,
41        primary_color: String,
42    }
43
44    #[derive(Clone, Debug, PartialEq)]
45    struct UserInfo {
46        name: String,
47        id: u32,
48    }
49
50    fn cleanup() {
51        clear_context::<Theme>();
52        clear_context::<UserInfo>();
53        clear_context::<i32>();
54    }
55
56    #[test]
57    fn test_provide_and_use_context() {
58        cleanup();
59
60        let theme = Theme {
61            dark_mode: true,
62            primary_color: "blue".to_string(),
63        };
64
65        provide_context(theme.clone());
66
67        let retrieved = use_context::<Theme>();
68        assert_eq!(retrieved, Some(theme));
69
70        cleanup();
71    }
72
73    #[test]
74    fn test_context_not_found() {
75        cleanup();
76
77        let result = use_context::<UserInfo>();
78        assert_eq!(result, None);
79
80        cleanup();
81    }
82
83    #[test]
84    fn test_multiple_contexts() {
85        cleanup();
86
87        let theme = Theme {
88            dark_mode: false,
89            primary_color: "green".to_string(),
90        };
91        let user = UserInfo {
92            name: "Alice".to_string(),
93            id: 42,
94        };
95
96        provide_context(theme.clone());
97        provide_context(user.clone());
98
99        assert_eq!(use_context::<Theme>(), Some(theme));
100        assert_eq!(use_context::<UserInfo>(), Some(user));
101
102        cleanup();
103    }
104
105    #[test]
106    fn test_use_context_or_default() {
107        cleanup();
108
109        let default_value = 100;
110        let result = use_context_or::<i32>(default_value);
111        assert_eq!(result, 100);
112
113        provide_context(42i32);
114        let result = use_context_or::<i32>(default_value);
115        assert_eq!(result, 42);
116
117        cleanup();
118    }
119
120    #[test]
121    fn test_context_override() {
122        cleanup();
123
124        provide_context(10i32);
125        assert_eq!(use_context::<i32>(), Some(10));
126
127        provide_context(20i32);
128        assert_eq!(use_context::<i32>(), Some(20));
129
130        cleanup();
131    }
132}