ricecoder_hooks/registry/
storage.rs1use crate::error::{HooksError, Result};
4use crate::types::Hook;
5use std::collections::HashMap;
6use std::sync::{Arc, RwLock};
7use uuid::Uuid;
8
9#[derive(Debug, Clone)]
11pub struct InMemoryHookRegistry {
12 hooks: Arc<RwLock<HashMap<String, Hook>>>,
13}
14
15impl InMemoryHookRegistry {
16 pub fn new() -> Self {
18 Self {
19 hooks: Arc::new(RwLock::new(HashMap::new())),
20 }
21 }
22}
23
24impl Default for InMemoryHookRegistry {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl super::HookRegistry for InMemoryHookRegistry {
31 fn register_hook(&mut self, mut hook: Hook) -> Result<String> {
32 if hook.id.is_empty() {
34 hook.id = Uuid::new_v4().to_string();
35 }
36
37 let hook_id = hook.id.clone();
38 let mut hooks = self.hooks.write().map_err(|e| {
39 HooksError::StorageError(format!("Failed to acquire write lock: {}", e))
40 })?;
41
42 hooks.insert(hook_id.clone(), hook);
43 Ok(hook_id)
44 }
45
46 fn unregister_hook(&self, hook_id: &str) -> Result<()> {
47 let mut hooks = self.hooks.write().map_err(|e| {
48 HooksError::StorageError(format!("Failed to acquire write lock: {}", e))
49 })?;
50
51 hooks
52 .remove(hook_id)
53 .ok_or_else(|| HooksError::HookNotFound(hook_id.to_string()))?;
54
55 Ok(())
56 }
57
58 fn get_hook(&self, hook_id: &str) -> Result<Hook> {
59 let hooks = self
60 .hooks
61 .read()
62 .map_err(|e| HooksError::StorageError(format!("Failed to acquire read lock: {}", e)))?;
63
64 hooks
65 .get(hook_id)
66 .cloned()
67 .ok_or_else(|| HooksError::HookNotFound(hook_id.to_string()))
68 }
69
70 fn list_hooks(&self) -> Result<Vec<Hook>> {
71 let hooks = self
72 .hooks
73 .read()
74 .map_err(|e| HooksError::StorageError(format!("Failed to acquire read lock: {}", e)))?;
75
76 Ok(hooks.values().cloned().collect())
77 }
78
79 fn list_hooks_for_event(&self, event: &str) -> Result<Vec<Hook>> {
80 let hooks = self
81 .hooks
82 .read()
83 .map_err(|e| HooksError::StorageError(format!("Failed to acquire read lock: {}", e)))?;
84
85 Ok(hooks
86 .values()
87 .filter(|h| h.event == event && h.enabled)
88 .cloned()
89 .collect())
90 }
91
92 fn enable_hook(&mut self, hook_id: &str) -> Result<()> {
93 let mut hooks = self.hooks.write().map_err(|e| {
94 HooksError::StorageError(format!("Failed to acquire write lock: {}", e))
95 })?;
96
97 let hook = hooks
98 .get_mut(hook_id)
99 .ok_or_else(|| HooksError::HookNotFound(hook_id.to_string()))?;
100
101 hook.enabled = true;
102 Ok(())
103 }
104
105 fn disable_hook(&mut self, hook_id: &str) -> Result<()> {
106 let mut hooks = self.hooks.write().map_err(|e| {
107 HooksError::StorageError(format!("Failed to acquire write lock: {}", e))
108 })?;
109
110 let hook = hooks
111 .get_mut(hook_id)
112 .ok_or_else(|| HooksError::HookNotFound(hook_id.to_string()))?;
113
114 hook.enabled = false;
115 Ok(())
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::registry::HookRegistry;
123 use crate::types::{Action, CommandAction};
124
125 fn create_test_hook(id: &str, event: &str, enabled: bool) -> Hook {
126 Hook {
127 id: id.to_string(),
128 name: format!("Test Hook {}", id),
129 description: None,
130 event: event.to_string(),
131 action: Action::Command(CommandAction {
132 command: "echo".to_string(),
133 args: vec!["test".to_string()],
134 timeout_ms: None,
135 capture_output: false,
136 }),
137 enabled,
138 tags: vec![],
139 metadata: serde_json::json!({}),
140 condition: None,
141 }
142 }
143
144 #[test]
145 fn test_register_hook() {
146 let mut registry = InMemoryHookRegistry::new();
147 let hook = create_test_hook("hook1", "file_saved", true);
148
149 let id = registry.register_hook(hook).unwrap();
150 assert!(!id.is_empty());
151 }
152
153 #[test]
154 fn test_register_hook_generates_id() {
155 let mut registry = InMemoryHookRegistry::new();
156 let mut hook = create_test_hook("", "file_saved", true);
157 hook.id = String::new();
158
159 let id = registry.register_hook(hook).unwrap();
160 assert!(!id.is_empty());
161 }
162
163 #[test]
164 fn test_get_hook() {
165 let mut registry = InMemoryHookRegistry::new();
166 let hook = create_test_hook("hook1", "file_saved", true);
167
168 registry.register_hook(hook.clone()).unwrap();
169 let retrieved = registry.get_hook("hook1").unwrap();
170
171 assert_eq!(retrieved.id, "hook1");
172 assert_eq!(retrieved.name, hook.name);
173 }
174
175 #[test]
176 fn test_get_hook_not_found() {
177 let registry = InMemoryHookRegistry::new();
178 let result = registry.get_hook("nonexistent");
179
180 assert!(result.is_err());
181 }
182
183 #[test]
184 fn test_list_hooks() {
185 let mut registry = InMemoryHookRegistry::new();
186 let hook1 = create_test_hook("hook1", "file_saved", true);
187 let hook2 = create_test_hook("hook2", "test_passed", true);
188
189 registry.register_hook(hook1).unwrap();
190 registry.register_hook(hook2).unwrap();
191
192 let hooks = registry.list_hooks().unwrap();
193 assert_eq!(hooks.len(), 2);
194 }
195
196 #[test]
197 fn test_list_hooks_for_event() {
198 let mut registry = InMemoryHookRegistry::new();
199 let hook1 = create_test_hook("hook1", "file_saved", true);
200 let hook2 = create_test_hook("hook2", "file_saved", true);
201 let hook3 = create_test_hook("hook3", "test_passed", true);
202
203 registry.register_hook(hook1).unwrap();
204 registry.register_hook(hook2).unwrap();
205 registry.register_hook(hook3).unwrap();
206
207 let hooks = registry.list_hooks_for_event("file_saved").unwrap();
208 assert_eq!(hooks.len(), 2);
209 }
210
211 #[test]
212 fn test_list_hooks_for_event_excludes_disabled() {
213 let mut registry = InMemoryHookRegistry::new();
214 let hook1 = create_test_hook("hook1", "file_saved", true);
215 let hook2 = create_test_hook("hook2", "file_saved", false);
216
217 registry.register_hook(hook1).unwrap();
218 registry.register_hook(hook2).unwrap();
219
220 let hooks = registry.list_hooks_for_event("file_saved").unwrap();
221 assert_eq!(hooks.len(), 1);
222 assert_eq!(hooks[0].id, "hook1");
223 }
224
225 #[test]
226 fn test_enable_hook() {
227 let mut registry = InMemoryHookRegistry::new();
228 let hook = create_test_hook("hook1", "file_saved", false);
229
230 registry.register_hook(hook).unwrap();
231 registry.enable_hook("hook1").unwrap();
232
233 let retrieved = registry.get_hook("hook1").unwrap();
234 assert!(retrieved.enabled);
235 }
236
237 #[test]
238 fn test_disable_hook() {
239 let mut registry = InMemoryHookRegistry::new();
240 let hook = create_test_hook("hook1", "file_saved", true);
241
242 registry.register_hook(hook).unwrap();
243 registry.disable_hook("hook1").unwrap();
244
245 let retrieved = registry.get_hook("hook1").unwrap();
246 assert!(!retrieved.enabled);
247 }
248
249 #[test]
250 fn test_unregister_hook() {
251 let mut registry = InMemoryHookRegistry::new();
252 let hook = create_test_hook("hook1", "file_saved", true);
253
254 registry.register_hook(hook).unwrap();
255 registry.unregister_hook("hook1").unwrap();
256
257 let result = registry.get_hook("hook1");
258 assert!(result.is_err());
259 }
260
261 #[test]
262 fn test_unregister_nonexistent_hook() {
263 let registry = InMemoryHookRegistry::new();
264 let result = registry.unregister_hook("nonexistent");
265
266 assert!(result.is_err());
267 }
268}