swarm_engine_core/
extensions.rs1use std::any::{Any, TypeId};
6use std::collections::HashMap;
7
8pub struct Extensions {
33 map: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
34}
35
36impl Extensions {
37 pub fn new() -> Self {
39 Self {
40 map: HashMap::new(),
41 }
42 }
43
44 pub fn insert<T: Send + Sync + 'static>(&mut self, value: T) {
46 self.map.insert(TypeId::of::<T>(), Box::new(value));
47 }
48
49 pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
51 self.map
52 .get(&TypeId::of::<T>())
53 .and_then(|boxed| boxed.downcast_ref())
54 }
55
56 pub fn get_mut<T: Send + Sync + 'static>(&mut self) -> Option<&mut T> {
58 self.map
59 .get_mut(&TypeId::of::<T>())
60 .and_then(|boxed| boxed.downcast_mut())
61 }
62
63 pub fn remove<T: Send + Sync + 'static>(&mut self) -> Option<T> {
65 self.map
66 .remove(&TypeId::of::<T>())
67 .and_then(|boxed| boxed.downcast().ok())
68 .map(|boxed| *boxed)
69 }
70
71 pub fn contains<T: Send + Sync + 'static>(&self) -> bool {
73 self.map.contains_key(&TypeId::of::<T>())
74 }
75
76 pub fn len(&self) -> usize {
78 self.map.len()
79 }
80
81 pub fn is_empty(&self) -> bool {
83 self.map.is_empty()
84 }
85}
86
87impl Default for Extensions {
88 fn default() -> Self {
89 Self::new()
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 struct TestResource {
98 value: i32,
99 }
100
101 struct AnotherResource {
102 name: String,
103 }
104
105 #[test]
106 fn test_insert_and_get() {
107 let mut extensions = Extensions::new();
108 extensions.insert(TestResource { value: 42 });
109
110 let resource = extensions.get::<TestResource>().unwrap();
111 assert_eq!(resource.value, 42);
112 }
113
114 #[test]
115 fn test_get_mut() {
116 let mut extensions = Extensions::new();
117 extensions.insert(TestResource { value: 10 });
118
119 {
120 let resource = extensions.get_mut::<TestResource>().unwrap();
121 resource.value = 20;
122 }
123
124 assert_eq!(extensions.get::<TestResource>().unwrap().value, 20);
125 }
126
127 #[test]
128 fn test_remove() {
129 let mut extensions = Extensions::new();
130 extensions.insert(TestResource { value: 100 });
131
132 let removed = extensions.remove::<TestResource>().unwrap();
133 assert_eq!(removed.value, 100);
134 assert!(extensions.get::<TestResource>().is_none());
135 }
136
137 #[test]
138 fn test_contains() {
139 let mut extensions = Extensions::new();
140 assert!(!extensions.contains::<TestResource>());
141
142 extensions.insert(TestResource { value: 0 });
143 assert!(extensions.contains::<TestResource>());
144 }
145
146 #[test]
147 fn test_multiple_types() {
148 let mut extensions = Extensions::new();
149 extensions.insert(TestResource { value: 1 });
150 extensions.insert(AnotherResource {
151 name: "test".to_string(),
152 });
153
154 assert_eq!(extensions.len(), 2);
155 assert_eq!(extensions.get::<TestResource>().unwrap().value, 1);
156 assert_eq!(extensions.get::<AnotherResource>().unwrap().name, "test");
157 }
158
159 #[test]
160 fn test_type_mismatch_returns_none() {
161 let mut extensions = Extensions::new();
162 extensions.insert(TestResource { value: 42 });
163
164 assert!(extensions.get::<AnotherResource>().is_none());
165 }
166}