bitwarden_sync/registry.rs
1//! Generic handler registry providing thread-safe handler storage.
2//!
3//! This module provides [`HandlerRegistry`], a reusable building block for
4//! storing and iterating over trait object handlers behind `Arc`.
5//!
6//! # Architecture
7//!
8//! The registry uses interior mutability via `RwLock` to allow
9//! handler registration without requiring mutable access. This enables
10//! sharing the registry across async boundaries and multiple components.
11//!
12//! # Execution Model
13//!
14//! Handlers are stored in registration order. The registry itself does not
15//! define execution semantics — callers iterate over handlers and decide
16//! how to dispatch them (fail-fast, best-effort, etc.).
17
18use std::sync::{Arc, RwLock};
19
20/// A thread-safe, ordered collection of handlers.
21///
22/// Supports registration via interior mutability and snapshot-based iteration.
23/// The type parameter `H` is typically a `dyn Trait` for trait object storage.
24///
25/// # Example
26///
27/// ```ignore
28/// let registry = HandlerRegistry::<dyn MyHandler>::new();
29/// registry.register(Arc::new(MyHandlerImpl));
30///
31/// for handler in ®istry.handlers() {
32/// handler.handle();
33/// }
34/// ```
35pub(crate) struct HandlerRegistry<H: ?Sized> {
36 handlers: RwLock<Vec<Arc<H>>>,
37}
38
39impl<H: ?Sized> HandlerRegistry<H> {
40 /// Create a new empty handler registry.
41 pub fn new() -> Self {
42 Self {
43 handlers: RwLock::new(Vec::new()),
44 }
45 }
46
47 /// Register a new handler.
48 ///
49 /// Handlers are stored in registration order.
50 pub fn register(&self, handler: Arc<H>) {
51 self.handlers
52 .write()
53 .expect("Handler registry lock poisoned")
54 .push(handler);
55 }
56
57 /// Get a snapshot of all registered handlers.
58 ///
59 /// Returns a cloned `Vec` so that iteration does not hold the lock.
60 pub fn handlers(&self) -> Vec<Arc<H>> {
61 self.handlers
62 .read()
63 .expect("Handler registry lock poisoned")
64 .clone()
65 }
66}
67
68impl<H: ?Sized> Default for HandlerRegistry<H> {
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 trait TestTrait: Send + Sync {
79 fn name(&self) -> &str;
80 }
81
82 struct Named(String);
83
84 impl TestTrait for Named {
85 fn name(&self) -> &str {
86 &self.0
87 }
88 }
89
90 #[test]
91 fn test_register_and_retrieve_handlers() {
92 let registry = HandlerRegistry::<dyn TestTrait>::new();
93 registry.register(Arc::new(Named("a".into())));
94 registry.register(Arc::new(Named("b".into())));
95
96 let handlers = registry.handlers();
97 assert_eq!(handlers.len(), 2);
98 assert_eq!(handlers[0].name(), "a");
99 assert_eq!(handlers[1].name(), "b");
100 }
101
102 #[test]
103 fn test_empty_registry_returns_empty_vec() {
104 let registry = HandlerRegistry::<dyn TestTrait>::new();
105 assert!(registry.handlers().is_empty());
106 }
107
108 #[test]
109 fn test_default_creates_empty_registry() {
110 let registry = HandlerRegistry::<dyn TestTrait>::default();
111 assert!(registry.handlers().is_empty());
112 }
113}