1use dashmap::DashMap;
2use std::any::{Any, TypeId};
3use std::sync::Arc;
4
5#[derive(Clone)]
26pub struct CodexSet {
27 map: Arc<DashMap<TypeId, Box<dyn Any + Send + Sync>>>,
28}
29
30impl Default for CodexSet {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl CodexSet {
37 #[inline]
39 pub fn new() -> Self {
40 Self {
41 map: Arc::new(DashMap::new()),
42 }
43 }
44
45 #[inline]
57 pub fn insert<T>(&self, value: T)
58 where
59 T: Send + Sync + 'static,
60 {
61 self.map.insert(TypeId::of::<T>(), Box::new(value));
62 }
63
64 #[inline]
66 pub fn contains<T>(&self) -> bool
67 where
68 T: Send + Sync + 'static,
69 {
70 self.map.contains_key(&TypeId::of::<T>())
71 }
72
73 pub fn with<T, F, R>(&self, f: F) -> Option<R>
90 where
91 T: Send + Sync + 'static,
92 F: FnOnce(&T) -> R,
93 {
94 let entry = self.map.get(&TypeId::of::<T>())?;
95 let value = entry.value().as_ref().downcast_ref::<T>()?;
96 Some(f(value))
97 }
98
99 pub fn with_mut<T, F, R>(&self, f: F) -> Option<R>
120 where
121 T: Send + Sync + 'static,
122 F: FnOnce(&mut T) -> R,
123 {
124 let mut entry = self.map.get_mut(&TypeId::of::<T>())?;
125 let value = entry.value_mut().as_mut().downcast_mut::<T>()?;
126 Some(f(value))
127 }
128
129 pub fn get_cloned<T>(&self) -> Option<T>
141 where
142 T: Clone + Send + Sync + 'static,
143 {
144 self.with::<T, _, _>(|v| v.clone())
145 }
146
147 pub fn take<T>(&self) -> Option<T>
160 where
161 T: Send + Sync + 'static,
162 {
163 self.map
164 .remove(&TypeId::of::<T>())
165 .and_then(|(_, boxed)| boxed.downcast::<T>().ok())
166 .map(|boxed| *boxed)
167 }
168}
169
170#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[derive(Clone, Debug, PartialEq)]
179 struct Config {
180 debug: bool,
181 }
182
183 #[test]
184 fn basic_operations() {
185 let set = CodexSet::new();
186
187 set.insert(Config { debug: true });
188 assert!(set.contains::<Config>());
189
190 let debug = set.with::<Config, _, _>(|config| config.debug).unwrap();
191 assert_eq!(debug, true);
192 }
193
194 #[test]
195 fn with_mut() {
196 let set = CodexSet::new();
197 set.insert(Config { debug: false });
198
199 set.with_mut::<Config, _, _>(|config| {
200 config.debug = true;
201 });
202
203 let debug = set.with::<Config, _, _>(|config| config.debug).unwrap();
204 assert_eq!(debug, true);
205 }
206
207 #[test]
208 fn get_cloned() {
209 let set = CodexSet::new();
210 set.insert(Config { debug: true });
211
212 let config = set.get_cloned::<Config>().unwrap();
213 assert_eq!(config.debug, true);
214
215 assert!(set.contains::<Config>());
217 }
218
219 #[test]
220 fn take() {
221 let set = CodexSet::new();
222 set.insert(Config { debug: true });
223
224 let config = set.take::<Config>().unwrap();
225 assert_eq!(config.debug, true);
226 assert!(!set.contains::<Config>());
227 }
228
229 #[test]
230 fn clone_shares_storage() {
231 let set = CodexSet::new();
232 set.insert(Config { debug: true });
233
234 let set2 = set.clone();
235
236 assert!(set2.contains::<Config>());
238 let debug = set2.with::<Config, _, _>(|c| c.debug).unwrap();
239 assert_eq!(debug, true);
240
241 set2.with_mut::<Config, _, _>(|config| {
243 config.debug = false;
244 });
245
246 let debug = set.with::<Config, _, _>(|c| c.debug).unwrap();
247 assert_eq!(debug, false);
248 }
249
250 #[tokio::test]
251 async fn clone_across_tasks() {
252 let set = CodexSet::new();
253 set.insert(Config { debug: true });
254
255 let set_clone = set.clone();
256
257 let handle = tokio::spawn(async move {
258 set_clone.insert(42u32);
259 set_clone.with::<Config, _, _>(|c| c.debug).unwrap()
260 });
261
262 let debug = handle.await.unwrap();
263 assert_eq!(debug, true);
264
265 assert_eq!(set.get_cloned::<u32>(), Some(42));
267 }
268
269 #[test]
270 fn missing_resource_returns_none() {
271 let set = CodexSet::new();
272
273 let result = set.with::<Config, _, _>(|c| c.debug);
274 assert!(result.is_none());
275 }
276
277 #[test]
278 fn multiple_types() {
279 let set = CodexSet::new();
280
281 set.insert(Config { debug: true });
282 set.insert(42u32);
283 set.insert("hello".to_string());
284
285 assert!(set.contains::<Config>());
286 assert!(set.contains::<u32>());
287 assert!(set.contains::<String>());
288
289 let debug = set.with::<Config, _, _>(|c| c.debug).unwrap();
290 assert_eq!(debug, true);
291
292 let num = set.get_cloned::<u32>().unwrap();
293 assert_eq!(num, 42);
294
295 let s = set.get_cloned::<String>().unwrap();
296 assert_eq!(s, "hello");
297 }
298}