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.entry(event_type.into()).or_default().push(Registered { id, f });
48 id
49 }
50
51 pub fn unregister(&self, id: HandlerId) -> bool {
54 let mut t = self.table.write().unwrap();
55 let mut removed = false;
56 for (_, list) in t.typed.iter_mut() {
57 if let Some(pos) = list.iter().position(|r| r.id == id) {
58 list.remove(pos);
59 removed = true;
60 }
61 }
62 t.typed.retain(|_, l| !l.is_empty());
63 if !removed {
64 let before = t.general.len();
65 t.general.retain(|r| r.id != id);
66 removed = before != t.general.len();
67 }
68 removed
69 }
70
71 pub fn register_general(&self, f: HandlerFn) -> HandlerId {
73 let id = HandlerId(self.next_id.fetch_add(1, Ordering::Relaxed));
74 self.table.write().unwrap().general.push(Registered { id, f });
75 id
76 }
77
78 pub fn unregister_general(&self, id: HandlerId) -> bool {
80 let mut t = self.table.write().unwrap();
81 let before = t.general.len();
82 t.general.retain(|r| r.id != id);
83 before != t.general.len()
84 }
85
86 pub(crate) fn snapshot(&self, event_type: &str) -> Vec<HandlerFn> {
88 let t = self.table.read().unwrap();
89 let mut out = Vec::new();
90 if let Some(list) = t.typed.get(event_type) {
91 out.extend(list.iter().map(|r| r.f.clone()));
92 }
93 out.extend(t.general.iter().map(|r| r.f.clone()));
94 out
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use std::sync::atomic::AtomicUsize;
102
103 #[test]
104 fn dispatch_and_unregister() {
105 let reg = HandlerRegistry::default();
106 let cnt = Arc::new(AtomicUsize::new(0));
107 let c1 = cnt.clone();
108 let id1 = reg.register("eTick.", Arc::new(move |_| {
109 c1.fetch_add(1, Ordering::SeqCst);
110 }));
111 let c2 = cnt.clone();
112 let id2 = reg.register("eTick.", Arc::new(move |_| {
113 c2.fetch_add(1, Ordering::SeqCst);
114 }));
115 let c3 = cnt.clone();
117 let g = reg.register_general(Arc::new(move |_| {
118 c3.fetch_add(10, Ordering::SeqCst);
119 }));
120 let snap = reg.snapshot("eTick.");
121 assert_eq!(snap.len(), 3, "2 typed + 1 general");
122 assert_eq!(cnt.load(Ordering::SeqCst), 0);
123
124 assert!(reg.unregister(g));
126 assert!(!reg.unregister(g));
127 let snap = reg.snapshot("eTick.");
128 assert_eq!(snap.len(), 2, "general withdrawn");
129 assert!(reg.unregister(id1));
130 assert!(reg.unregister(id2));
131 let snap = reg.snapshot("eTick.");
133 assert!(snap.is_empty(), "empty handler list should be recycled");
134 let c4 = cnt.clone();
136 let id_re = reg.register("eTick.", Arc::new(move |_| {
137 c4.fetch_add(1, Ordering::SeqCst);
138 }));
139 assert_eq!(reg.snapshot("eTick.").len(), 1, "re-register rebuilds list");
140 assert!(reg.unregister(id_re));
141 }
142}