Skip to main content

aura_agent/runtime/
registry.rs

1//! Effect registry for dynamic handler lookup
2//!
3//! Provides dynamic registry for effect handlers, enabling runtime composition
4//! and protocol-specific handler selection in the authority-centric architecture.
5//!
6//! # Blocking Lock Usage
7//!
8//! Uses `std::sync::RwLock` (not tokio or parking_lot) because:
9//! 1. Lock poisoning detection is required - the code handles `PoisonError` explicitly
10//! 2. Operations are brief HashMap lookups/inserts (sub-millisecond)
11//! 3. No `.await` points inside lock scope
12
13#![allow(clippy::disallowed_types)]
14
15use aura_core::effects::ExecutionMode;
16use std::collections::{HashMap, HashSet};
17use std::fmt;
18use std::sync::{Arc, RwLock};
19
20use super::executor::EffectHandler;
21
22/// Typed effect categories for registry keys.
23#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
24pub enum EffectType {
25    Crypto,
26    Storage,
27    Transport,
28    Journal,
29}
30
31impl EffectType {
32    pub const fn as_str(self) -> &'static str {
33        match self {
34            EffectType::Crypto => "crypto",
35            EffectType::Storage => "storage",
36            EffectType::Transport => "transport",
37            EffectType::Journal => "journal",
38        }
39    }
40}
41
42impl fmt::Display for EffectType {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        f.write_str(self.as_str())
45    }
46}
47
48/// Typed operation identifier for registry keys.
49#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
50pub struct EffectOperation(&'static str);
51
52impl EffectOperation {
53    pub const fn new(name: &'static str) -> Self {
54        Self(name)
55    }
56
57    pub const fn as_str(self) -> &'static str {
58        self.0
59    }
60}
61
62impl fmt::Display for EffectOperation {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        f.write_str(self.0)
65    }
66}
67
68impl From<&'static str> for EffectOperation {
69    fn from(value: &'static str) -> Self {
70        Self(value)
71    }
72}
73
74/// Dynamic registry for effect handlers
75#[derive(Debug)]
76pub struct EffectRegistry {
77    shared: Arc<EffectRegistryShared>,
78    execution_mode: ExecutionMode,
79}
80
81#[derive(Debug)]
82struct EffectRegistryShared {
83    handlers: RwLock<HashMap<EffectKey, Arc<dyn std::any::Any + Send + Sync>>>,
84}
85
86impl EffectRegistry {
87    /// Create a new effect registry
88    pub fn new(execution_mode: ExecutionMode) -> Self {
89        Self {
90            shared: Arc::new(EffectRegistryShared {
91                handlers: RwLock::new(HashMap::new()),
92            }),
93            execution_mode,
94        }
95    }
96
97    /// Register a typed effect handler for dynamic dispatch.
98    pub fn register_effect_handler<T, H>(
99        &self,
100        effect_type: EffectType,
101        operation: EffectOperation,
102        handler: H,
103    ) -> Result<(), EffectRegistryError>
104    where
105        T: Send + Sync + 'static,
106        H: EffectHandler<T> + 'static,
107    {
108        let handler: Arc<dyn EffectHandler<T>> = Arc::new(handler);
109        self.register(effect_type, operation, handler)
110    }
111
112    /// Retrieve a typed effect handler for dynamic dispatch.
113    pub fn get_effect_handler<T: Send + Sync + 'static>(
114        &self,
115        effect_type: EffectType,
116        operation: EffectOperation,
117    ) -> Result<Option<Arc<dyn EffectHandler<T>>>, EffectRegistryError> {
118        Ok(self
119            .get::<Arc<dyn EffectHandler<T>>>(effect_type, operation)?
120            .map(|handler| handler.as_ref().clone()))
121    }
122
123    /// Register an effect handler
124    pub fn register<T: Send + Sync + 'static>(
125        &self,
126        effect_type: EffectType,
127        operation: EffectOperation,
128        handler: T,
129    ) -> Result<(), EffectRegistryError> {
130        let key = EffectKey::new(effect_type, operation);
131
132        let mut handlers = self
133            .shared
134            .handlers
135            .write()
136            .map_err(|_| EffectRegistryError::LockError)?;
137
138        handlers.insert(key, Arc::new(handler));
139        Ok(())
140    }
141
142    /// Get an effect handler
143    pub fn get<T: Send + Sync + 'static>(
144        &self,
145        effect_type: EffectType,
146        operation: EffectOperation,
147    ) -> Result<Option<Arc<T>>, EffectRegistryError> {
148        let key = EffectKey::new(effect_type, operation);
149
150        let handlers = self
151            .shared
152            .handlers
153            .read()
154            .map_err(|_| EffectRegistryError::LockError)?;
155
156        Ok(handlers
157            .get(&key)
158            .and_then(|handler| handler.clone().downcast::<T>().ok()))
159    }
160
161    /// Check if an effect handler is registered
162    pub fn has_handler(&self, effect_type: EffectType, operation: EffectOperation) -> bool {
163        let key = EffectKey::new(effect_type, operation);
164
165        self.shared
166            .handlers
167            .read()
168            .map(|handlers| handlers.contains_key(&key))
169            .unwrap_or(false)
170    }
171
172    /// Get all registered effect types
173    pub fn effect_types(&self) -> Vec<EffectType> {
174        self.shared
175            .handlers
176            .read()
177            .map(|handlers| {
178                handlers
179                    .keys()
180                    .map(|key| key.effect_type)
181                    .collect::<HashSet<_>>()
182                    .into_iter()
183                    .collect()
184            })
185            .unwrap_or_default()
186    }
187
188    /// Get operations for an effect type
189    pub fn operations(&self, effect_type: EffectType) -> Vec<EffectOperation> {
190        self.shared
191            .handlers
192            .read()
193            .map(|handlers| {
194                handlers
195                    .keys()
196                    .filter(|key| key.effect_type == effect_type)
197                    .map(|key| key.operation)
198                    .collect()
199            })
200            .unwrap_or_default()
201    }
202
203    /// Clear all handlers
204    pub fn clear(&self) -> Result<(), EffectRegistryError> {
205        let mut handlers = self
206            .shared
207            .handlers
208            .write()
209            .map_err(|_| EffectRegistryError::LockError)?;
210
211        handlers.clear();
212        Ok(())
213    }
214
215    /// Get the execution mode
216    pub fn execution_mode(&self) -> ExecutionMode {
217        self.execution_mode
218    }
219}
220
221impl Clone for EffectRegistry {
222    fn clone(&self) -> Self {
223        Self {
224            shared: Arc::clone(&self.shared),
225            execution_mode: self.execution_mode,
226        }
227    }
228}
229
230/// Key for indexing effect handlers
231#[derive(Debug, Clone, Hash, Eq, PartialEq)]
232struct EffectKey {
233    effect_type: EffectType,
234    operation: EffectOperation,
235}
236
237impl EffectKey {
238    fn new(effect_type: EffectType, operation: EffectOperation) -> Self {
239        Self {
240            effect_type,
241            operation,
242        }
243    }
244}
245
246/// Errors that can occur during registry operations
247#[derive(Debug, thiserror::Error)]
248pub enum EffectRegistryError {
249    #[error("Registry lock error")]
250    LockError,
251    #[error("Handler not found: {effect_type}.{operation}")]
252    HandlerNotFound {
253        effect_type: EffectType,
254        operation: EffectOperation,
255    },
256    #[error("Handler type mismatch")]
257    TypeMismatch,
258    #[error("Registration failed: {reason}")]
259    RegistrationFailed { reason: String },
260}
261
262/// Extension trait for EffectRegistry with convenience methods
263pub trait EffectRegistryExt {
264    /// Register a crypto handler
265    fn register_crypto_handler<T: Send + Sync + 'static>(
266        &self,
267        operation: EffectOperation,
268        handler: T,
269    ) -> Result<(), EffectRegistryError>;
270
271    /// Register a storage handler
272    fn register_storage_handler<T: Send + Sync + 'static>(
273        &self,
274        operation: EffectOperation,
275        handler: T,
276    ) -> Result<(), EffectRegistryError>;
277
278    /// Register a transport handler
279    fn register_transport_handler<T: Send + Sync + 'static>(
280        &self,
281        operation: EffectOperation,
282        handler: T,
283    ) -> Result<(), EffectRegistryError>;
284
285    /// Register a journal handler
286    fn register_journal_handler<T: Send + Sync + 'static>(
287        &self,
288        operation: EffectOperation,
289        handler: T,
290    ) -> Result<(), EffectRegistryError>;
291}
292
293impl EffectRegistryExt for EffectRegistry {
294    fn register_crypto_handler<T: Send + Sync + 'static>(
295        &self,
296        operation: EffectOperation,
297        handler: T,
298    ) -> Result<(), EffectRegistryError> {
299        self.register(EffectType::Crypto, operation, handler)
300    }
301
302    fn register_storage_handler<T: Send + Sync + 'static>(
303        &self,
304        operation: EffectOperation,
305        handler: T,
306    ) -> Result<(), EffectRegistryError> {
307        self.register(EffectType::Storage, operation, handler)
308    }
309
310    fn register_transport_handler<T: Send + Sync + 'static>(
311        &self,
312        operation: EffectOperation,
313        handler: T,
314    ) -> Result<(), EffectRegistryError> {
315        self.register(EffectType::Transport, operation, handler)
316    }
317
318    fn register_journal_handler<T: Send + Sync + 'static>(
319        &self,
320        operation: EffectOperation,
321        handler: T,
322    ) -> Result<(), EffectRegistryError> {
323        self.register(EffectType::Journal, operation, handler)
324    }
325}