Skip to main content

oxihuman_core/
event_dispatch.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Synchronous event dispatcher with typed handler table.
6
7use std::collections::HashMap;
8
9/// Opaque handler identifier.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11#[allow(dead_code)]
12pub struct HandlerId(u32);
13
14/// A record of a dispatched event.
15#[derive(Debug, Clone)]
16#[allow(dead_code)]
17pub struct DispatchRecord {
18    pub event_type: String,
19    pub handled: bool,
20    pub handler_count: usize,
21}
22
23/// Event dispatcher.
24#[derive(Debug)]
25#[allow(dead_code)]
26pub struct EventDispatcher {
27    next_id: u32,
28    handlers: HashMap<String, Vec<(HandlerId, String)>>,
29    dispatch_count: u64,
30}
31
32/// Create a new EventDispatcher.
33#[allow(dead_code)]
34pub fn new_dispatcher() -> EventDispatcher {
35    EventDispatcher {
36        next_id: 1,
37        handlers: HashMap::new(),
38        dispatch_count: 0,
39    }
40}
41
42/// Register a named handler for an event type. Returns HandlerId.
43#[allow(dead_code)]
44pub fn register_handler(
45    d: &mut EventDispatcher,
46    event_type: &str,
47    handler_name: &str,
48) -> HandlerId {
49    let id = HandlerId(d.next_id);
50    d.next_id += 1;
51    d.handlers
52        .entry(event_type.to_string())
53        .or_default()
54        .push((id, handler_name.to_string()));
55    id
56}
57
58/// Unregister a handler by id.
59#[allow(dead_code)]
60pub fn unregister_handler(d: &mut EventDispatcher, event_type: &str, id: HandlerId) -> bool {
61    if let Some(list) = d.handlers.get_mut(event_type) {
62        let before = list.len();
63        list.retain(|(hid, _)| *hid != id);
64        return list.len() < before;
65    }
66    false
67}
68
69/// Dispatch an event; returns number of handlers invoked.
70#[allow(dead_code)]
71pub fn dispatch(d: &mut EventDispatcher, event_type: &str) -> DispatchRecord {
72    let count = d.handlers.get(event_type).map(|v| v.len()).unwrap_or(0);
73    d.dispatch_count += 1;
74    DispatchRecord {
75        event_type: event_type.to_string(),
76        handled: count > 0,
77        handler_count: count,
78    }
79}
80
81/// Number of handlers for a given event type.
82#[allow(dead_code)]
83pub fn handler_count(d: &EventDispatcher, event_type: &str) -> usize {
84    d.handlers.get(event_type).map(|v| v.len()).unwrap_or(0)
85}
86
87/// Total number of dispatches executed.
88#[allow(dead_code)]
89pub fn dispatch_count(d: &EventDispatcher) -> u64 {
90    d.dispatch_count
91}
92
93/// All registered event types.
94#[allow(dead_code)]
95pub fn registered_event_types(d: &EventDispatcher) -> Vec<String> {
96    d.handlers.keys().cloned().collect()
97}
98
99/// Clear all handlers for an event type.
100#[allow(dead_code)]
101pub fn clear_handlers(d: &mut EventDispatcher, event_type: &str) {
102    d.handlers.remove(event_type);
103}
104
105/// Clear all handlers across all event types.
106#[allow(dead_code)]
107pub fn clear_all_handlers(d: &mut EventDispatcher) {
108    d.handlers.clear();
109}
110
111/// Names of all handlers for a type.
112#[allow(dead_code)]
113pub fn handler_names(d: &EventDispatcher, event_type: &str) -> Vec<String> {
114    d.handlers
115        .get(event_type)
116        .map(|v| v.iter().map(|(_, n)| n.clone()).collect())
117        .unwrap_or_default()
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn test_register_and_dispatch() {
126        let mut d = new_dispatcher();
127        register_handler(&mut d, "click", "on_click");
128        let rec = dispatch(&mut d, "click");
129        assert!(rec.handled);
130        assert_eq!(rec.handler_count, 1);
131    }
132
133    #[test]
134    fn test_dispatch_no_handlers() {
135        let mut d = new_dispatcher();
136        let rec = dispatch(&mut d, "unknown");
137        assert!(!rec.handled);
138        assert_eq!(rec.handler_count, 0);
139    }
140
141    #[test]
142    fn test_unregister() {
143        let mut d = new_dispatcher();
144        let id = register_handler(&mut d, "resize", "h1");
145        assert!(unregister_handler(&mut d, "resize", id));
146        assert_eq!(handler_count(&d, "resize"), 0);
147    }
148
149    #[test]
150    fn test_multiple_handlers() {
151        let mut d = new_dispatcher();
152        register_handler(&mut d, "key", "h1");
153        register_handler(&mut d, "key", "h2");
154        assert_eq!(handler_count(&d, "key"), 2);
155    }
156
157    #[test]
158    fn test_dispatch_count() {
159        let mut d = new_dispatcher();
160        dispatch(&mut d, "a");
161        dispatch(&mut d, "b");
162        assert_eq!(dispatch_count(&d), 2);
163    }
164
165    #[test]
166    fn test_clear_handlers() {
167        let mut d = new_dispatcher();
168        register_handler(&mut d, "e", "h");
169        clear_handlers(&mut d, "e");
170        assert_eq!(handler_count(&d, "e"), 0);
171    }
172
173    #[test]
174    fn test_registered_event_types() {
175        let mut d = new_dispatcher();
176        register_handler(&mut d, "a", "h1");
177        register_handler(&mut d, "b", "h2");
178        let types = registered_event_types(&d);
179        assert_eq!(types.len(), 2);
180    }
181
182    #[test]
183    fn test_handler_names() {
184        let mut d = new_dispatcher();
185        register_handler(&mut d, "ev", "alpha");
186        register_handler(&mut d, "ev", "beta");
187        let names = handler_names(&d, "ev");
188        assert!(names.contains(&"alpha".to_string()));
189        assert!(names.contains(&"beta".to_string()));
190    }
191
192    #[test]
193    fn test_clear_all() {
194        let mut d = new_dispatcher();
195        register_handler(&mut d, "x", "h");
196        register_handler(&mut d, "y", "h");
197        clear_all_handlers(&mut d);
198        assert_eq!(registered_event_types(&d).len(), 0);
199    }
200}