1use crate::event::Event;
9use std::collections::HashMap;
10use std::sync::atomic::{AtomicU64, Ordering};
11use std::sync::{Arc, RwLock};
12
13pub type HandlerFn = Arc<dyn Fn(&Event) + Send + Sync>;
15
16#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
18pub struct HandlerId(pub(crate) u64);
19
20struct Registered {
21 id: HandlerId,
22 f: HandlerFn,
23}
24
25#[derive(Default)]
26struct Table {
27 typed: HashMap<String, Vec<Registered>>,
29 general: Vec<Registered>,
31}
32
33#[derive(Default)]
35pub struct HandlerRegistry {
36 table: RwLock<Table>,
37 next_id: AtomicU64,
38}
39
40impl HandlerRegistry {
41 pub fn register(&self, event_type: impl Into<String>, f: HandlerFn) -> HandlerId {
45 let id = HandlerId(self.next_id.fetch_add(1, Ordering::Relaxed));
46 let mut t = self.table.write().unwrap();
47 t.typed
48 .entry(event_type.into())
49 .or_default()
50 .push(Registered { id, f });
51 id
52 }
53
54 pub fn unregister(&self, id: HandlerId) -> bool {
57 let mut t = self.table.write().unwrap();
58 let mut removed = false;
59 for (_, list) in t.typed.iter_mut() {
60 if let Some(pos) = list.iter().position(|r| r.id == id) {
61 list.remove(pos);
62 removed = true;
63 }
64 }
65 t.typed.retain(|_, l| !l.is_empty());
66 if !removed {
67 let before = t.general.len();
68 t.general.retain(|r| r.id != id);
69 removed = before != t.general.len();
70 }
71 removed
72 }
73
74 pub fn register_general(&self, f: HandlerFn) -> HandlerId {
76 let id = HandlerId(self.next_id.fetch_add(1, Ordering::Relaxed));
77 self.table
78 .write()
79 .unwrap()
80 .general
81 .push(Registered { id, f });
82 id
83 }
84
85 pub fn unregister_general(&self, id: HandlerId) -> bool {
87 let mut t = self.table.write().unwrap();
88 let before = t.general.len();
89 t.general.retain(|r| r.id != id);
90 before != t.general.len()
91 }
92
93 pub(crate) fn snapshot(&self, event_type: &str) -> Vec<HandlerFn> {
95 let t = self.table.read().unwrap();
96 let mut out = Vec::new();
97 if let Some(list) = t.typed.get(event_type) {
98 out.extend(list.iter().map(|r| r.f.clone()));
99 }
100 out.extend(t.general.iter().map(|r| r.f.clone()));
101 out
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use std::sync::atomic::AtomicUsize;
109
110 #[test]
111 fn dispatch_and_unregister() {
112 let reg = HandlerRegistry::default();
113 let cnt = Arc::new(AtomicUsize::new(0));
114 let c1 = cnt.clone();
115 let id1 = reg.register(
116 "eTick.",
117 Arc::new(move |_| {
118 c1.fetch_add(1, Ordering::SeqCst);
119 }),
120 );
121 let c2 = cnt.clone();
122 let id2 = reg.register(
123 "eTick.",
124 Arc::new(move |_| {
125 c2.fetch_add(1, Ordering::SeqCst);
126 }),
127 );
128 let c3 = cnt.clone();
130 let g = reg.register_general(Arc::new(move |_| {
131 c3.fetch_add(10, Ordering::SeqCst);
132 }));
133 let snap = reg.snapshot("eTick.");
134 assert_eq!(snap.len(), 3, "2 typed + 1 general");
135 assert_eq!(cnt.load(Ordering::SeqCst), 0);
136
137 assert!(reg.unregister(g));
139 assert!(!reg.unregister(g));
140 let snap = reg.snapshot("eTick.");
141 assert_eq!(snap.len(), 2, "general withdrawn");
142 assert!(reg.unregister(id1));
143 assert!(reg.unregister(id2));
144 let snap = reg.snapshot("eTick.");
146 assert!(snap.is_empty(), "empty handler list should be recycled");
147 let c4 = cnt.clone();
149 let id_re = reg.register(
150 "eTick.",
151 Arc::new(move |_| {
152 c4.fetch_add(1, Ordering::SeqCst);
153 }),
154 );
155 assert_eq!(reg.snapshot("eTick.").len(), 1, "re-register rebuilds list");
156 assert!(reg.unregister(id_re));
157 }
158}