sentinel_common/
registry.rs1use std::collections::HashMap;
23use std::sync::Arc;
24use tokio::sync::RwLock;
25
26#[derive(Debug)]
32pub struct Registry<T> {
33 items: Arc<RwLock<HashMap<String, Arc<T>>>>,
34}
35
36impl<T> Registry<T> {
37 pub fn new() -> Self {
39 Self {
40 items: Arc::new(RwLock::new(HashMap::new())),
41 }
42 }
43
44 pub fn with_capacity(capacity: usize) -> Self {
46 Self {
47 items: Arc::new(RwLock::new(HashMap::with_capacity(capacity))),
48 }
49 }
50
51 pub fn from_map(map: HashMap<String, Arc<T>>) -> Self {
53 Self {
54 items: Arc::new(RwLock::new(map)),
55 }
56 }
57
58 pub async fn get(&self, id: &str) -> Option<Arc<T>> {
60 self.items.read().await.get(id).cloned()
61 }
62
63 pub async fn contains(&self, id: &str) -> bool {
65 self.items.read().await.contains_key(id)
66 }
67
68 pub async fn insert(&self, id: impl Into<String>, item: Arc<T>) -> Option<Arc<T>> {
70 self.items.write().await.insert(id.into(), item)
71 }
72
73 pub async fn remove(&self, id: &str) -> Option<Arc<T>> {
75 self.items.write().await.remove(id)
76 }
77
78 pub async fn keys(&self) -> Vec<String> {
80 self.items.read().await.keys().cloned().collect()
81 }
82
83 pub async fn len(&self) -> usize {
85 self.items.read().await.len()
86 }
87
88 pub async fn is_empty(&self) -> bool {
90 self.items.read().await.is_empty()
91 }
92
93 pub async fn clear(&self) {
95 self.items.write().await.clear()
96 }
97
98 pub async fn replace(&self, new_items: HashMap<String, Arc<T>>) -> HashMap<String, Arc<T>> {
100 let mut guard = self.items.write().await;
101 std::mem::replace(&mut *guard, new_items)
102 }
103
104 pub async fn snapshot(&self) -> HashMap<String, Arc<T>> {
106 self.items.read().await.clone()
107 }
108
109 pub async fn with_read<F, R>(&self, f: F) -> R
113 where
114 F: FnOnce(&HashMap<String, Arc<T>>) -> R,
115 {
116 f(&*self.items.read().await)
117 }
118
119 pub async fn with_write<F, R>(&self, f: F) -> R
123 where
124 F: FnOnce(&mut HashMap<String, Arc<T>>) -> R,
125 {
126 f(&mut *self.items.write().await)
127 }
128}
129
130impl<T> Default for Registry<T> {
131 fn default() -> Self {
132 Self::new()
133 }
134}
135
136impl<T> Clone for Registry<T> {
137 fn clone(&self) -> Self {
138 Self {
139 items: Arc::clone(&self.items),
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[tokio::test]
149 async fn test_registry_basic_operations() {
150 let registry: Registry<String> = Registry::new();
151
152 registry
154 .insert("key1", Arc::new("value1".to_string()))
155 .await;
156 registry
157 .insert("key2", Arc::new("value2".to_string()))
158 .await;
159
160 assert_eq!(
162 registry.get("key1").await.as_deref().map(|s| s.as_str()),
163 Some("value1")
164 );
165 assert!(registry.get("nonexistent").await.is_none());
166
167 assert!(registry.contains("key1").await);
169 assert!(!registry.contains("nonexistent").await);
170
171 assert_eq!(registry.len().await, 2);
173
174 let keys = registry.keys().await;
176 assert!(keys.contains(&"key1".to_string()));
177 assert!(keys.contains(&"key2".to_string()));
178
179 let removed = registry.remove("key1").await;
181 assert!(removed.is_some());
182 assert!(!registry.contains("key1").await);
183 assert_eq!(registry.len().await, 1);
184
185 registry.clear().await;
187 assert!(registry.is_empty().await);
188 }
189
190 #[tokio::test]
191 async fn test_registry_replace() {
192 let registry: Registry<i32> = Registry::new();
193 registry.insert("a", Arc::new(1)).await;
194 registry.insert("b", Arc::new(2)).await;
195
196 let mut new_items = HashMap::new();
197 new_items.insert("c".to_string(), Arc::new(3));
198 new_items.insert("d".to_string(), Arc::new(4));
199
200 let old = registry.replace(new_items).await;
201
202 assert!(old.contains_key("a"));
203 assert!(old.contains_key("b"));
204 assert!(!registry.contains("a").await);
205 assert!(registry.contains("c").await);
206 assert!(registry.contains("d").await);
207 }
208
209 #[tokio::test]
210 async fn test_registry_with_read() {
211 let registry: Registry<i32> = Registry::new();
212 registry.insert("a", Arc::new(1)).await;
213 registry.insert("b", Arc::new(2)).await;
214
215 let sum = registry
216 .with_read(|items| items.values().map(|v| **v).sum::<i32>())
217 .await;
218
219 assert_eq!(sum, 3);
220 }
221
222 #[tokio::test]
223 async fn test_registry_clone_shares_data() {
224 let registry1: Registry<String> = Registry::new();
225 let registry2 = registry1.clone();
226
227 registry1.insert("key", Arc::new("value".to_string())).await;
228
229 assert!(registry2.contains("key").await);
231 }
232}