cognis_core/
extensions.rs1use std::any::{Any, TypeId};
7use std::collections::HashMap;
8use std::fmt;
9
10#[derive(Default)]
13pub struct Extensions {
14 map: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
15}
16
17impl Extensions {
18 pub fn new() -> Self {
20 Self::default()
21 }
22
23 pub fn insert<T: Any + Send + Sync + 'static>(&mut self, value: T) -> Option<T> {
25 self.map
26 .insert(TypeId::of::<T>(), Box::new(value))
27 .and_then(|prev| prev.downcast::<T>().ok().map(|b| *b))
28 }
29
30 pub fn get<T: Any + Send + Sync + 'static>(&self) -> Option<&T> {
32 self.map
33 .get(&TypeId::of::<T>())
34 .and_then(|b| b.downcast_ref::<T>())
35 }
36
37 pub fn get_mut<T: Any + Send + Sync + 'static>(&mut self) -> Option<&mut T> {
39 self.map
40 .get_mut(&TypeId::of::<T>())
41 .and_then(|b| b.downcast_mut::<T>())
42 }
43
44 pub fn remove<T: Any + Send + Sync + 'static>(&mut self) -> Option<T> {
46 self.map
47 .remove(&TypeId::of::<T>())
48 .and_then(|b| b.downcast::<T>().ok().map(|b| *b))
49 }
50
51 pub fn contains<T: Any + Send + Sync + 'static>(&self) -> bool {
53 self.map.contains_key(&TypeId::of::<T>())
54 }
55
56 pub fn len(&self) -> usize {
58 self.map.len()
59 }
60
61 pub fn is_empty(&self) -> bool {
63 self.map.is_empty()
64 }
65
66 pub fn clear(&mut self) {
68 self.map.clear();
69 }
70}
71
72impl fmt::Debug for Extensions {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 f.debug_struct("Extensions")
75 .field("len", &self.map.len())
76 .finish()
77 }
78}
79
80impl Clone for Extensions {
81 fn clone(&self) -> Self {
85 Self::default()
86 }
87}
88
89impl serde::Serialize for Extensions {
90 fn serialize<Se: serde::Serializer>(&self, s: Se) -> std::result::Result<Se::Ok, Se::Error> {
95 use serde::ser::SerializeMap;
96 s.serialize_map(Some(0))?.end()
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[derive(Debug, PartialEq)]
105 struct UserId(u32);
106
107 #[derive(Debug, PartialEq)]
108 struct Tag(&'static str);
109
110 #[test]
111 fn insert_get_remove() {
112 let mut ex = Extensions::new();
113 assert!(ex.is_empty());
114
115 assert!(ex.insert(UserId(42)).is_none());
116 assert_eq!(ex.get::<UserId>(), Some(&UserId(42)));
117 assert_eq!(ex.len(), 1);
118
119 let prev = ex.insert(UserId(99));
120 assert_eq!(prev, Some(UserId(42)));
121 assert_eq!(ex.get::<UserId>(), Some(&UserId(99)));
122
123 let removed = ex.remove::<UserId>();
124 assert_eq!(removed, Some(UserId(99)));
125 assert!(ex.get::<UserId>().is_none());
126 }
127
128 #[test]
129 fn distinct_types_coexist() {
130 let mut ex = Extensions::new();
131 ex.insert(UserId(1));
132 ex.insert(Tag("admin"));
133 assert_eq!(ex.get::<UserId>(), Some(&UserId(1)));
134 assert_eq!(ex.get::<Tag>(), Some(&Tag("admin")));
135 assert_eq!(ex.len(), 2);
136 }
137
138 #[test]
139 fn clone_yields_empty() {
140 let mut ex = Extensions::new();
141 ex.insert(UserId(1));
142 let cloned = ex.clone();
143 assert!(cloned.is_empty());
144 }
145}