httpward_core/core/context/
extensions.rs1use std::any::Any;
5use std::sync::Arc;
6use parking_lot::RwLock;
7use std::collections::HashMap;
8
9#[derive(Clone)]
12pub struct ExtensionsMap {
13 inner: Arc<RwLock<HashMap<String, Arc<dyn Any + Send + Sync>>>>,
14}
15
16impl ExtensionsMap {
17 pub fn new() -> Self {
19 Self {
20 inner: Arc::new(RwLock::new(HashMap::new())),
21 }
22 }
23
24 pub fn insert<T: Any + Send + Sync + 'static>(&self, key: impl Into<String>, value: T) {
36 self.inner.write().insert(key.into(), Arc::new(value));
37 }
38
39 pub fn get<T: Any + Send + Sync + 'static>(&self, key: &str) -> Option<Arc<T>> {
55 let inner = self.inner.read();
56 let value = inner.get(key)?.clone();
57 value.downcast::<T>().ok()
58 }
59
60 pub fn contains_key(&self, key: &str) -> bool {
62 self.inner.read().contains_key(key)
63 }
64
65 pub fn remove(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
67 self.inner.write().remove(key)
68 }
69
70 pub fn clear(&self) {
72 self.inner.write().clear();
73 }
74
75 pub fn len(&self) -> usize {
77 self.inner.read().len()
78 }
79
80 pub fn is_empty(&self) -> bool {
82 self.inner.read().is_empty()
83 }
84}
85
86impl Default for ExtensionsMap {
87 fn default() -> Self {
88 Self::new()
89 }
90}
91
92impl std::fmt::Debug for ExtensionsMap {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 f.debug_struct("ExtensionsMap")
95 .field("count", &self.len())
96 .finish()
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn test_insert_and_get() {
106 let ext = ExtensionsMap::new();
107
108 ext.insert("user_id", 123u64);
110 assert_eq!(ext.get::<u64>("user_id").map(|v| *v), Some(123u64));
111
112 ext.insert("session", "abc123".to_string());
114 assert_eq!(ext.get::<String>("session").map(|v| (*v).clone()), Some("abc123".to_string()));
115 }
116
117 #[test]
118 fn test_type_mismatch() {
119 let ext = ExtensionsMap::new();
120 ext.insert("value", 42u64);
121
122 assert_eq!(ext.get::<String>("value"), None);
124 }
125
126 #[test]
127 fn test_missing_key() {
128 let ext = ExtensionsMap::new();
129 assert_eq!(ext.get::<u64>("nonexistent"), None);
130 }
131
132 #[test]
133 fn test_contains_key() {
134 let ext = ExtensionsMap::new();
135 ext.insert("key1", "value".to_string());
136
137 assert!(ext.contains_key("key1"));
138 assert!(!ext.contains_key("key2"));
139 }
140
141 #[test]
142 fn test_remove() {
143 let ext = ExtensionsMap::new();
144 ext.insert("key", 42u64);
145
146 assert!(ext.remove("key").is_some());
147 assert!(ext.get::<u64>("key").is_none());
148 }
149
150 #[test]
151 fn test_clone_shares_data() {
152 let ext1 = ExtensionsMap::new();
153 ext1.insert("shared", 999u64);
154
155 let ext2 = ext1.clone();
156
157 assert_eq!(ext2.get::<u64>("shared").map(|v| *v), Some(999u64));
159 }
160
161 #[test]
162 fn test_multiple_types() {
163 let ext = ExtensionsMap::new();
164
165 ext.insert("int", 42u64);
166 ext.insert("string", "hello".to_string());
167 ext.insert("float", 3.14f64);
168
169 assert_eq!(ext.len(), 3);
170 assert_eq!(ext.get::<u64>("int").map(|v| *v), Some(42u64));
171 assert_eq!(ext.get::<String>("string").map(|v| (*v).clone()), Some("hello".to_string()));
172 assert_eq!(ext.get::<f64>("float").map(|v| *v), Some(3.14f64));
173 }
174}
175