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}