handler_map/lib.rs
1// This Source Code Form is subject to the terms of the
2// Mozilla Public License, v. 2.0. If a copy of the MPL was
3// not distributed with this file, You can obtain one at
4// http://mozilla.org/MPL/2.0/.
5
6//! Crate which contains a "handler map", a structure that maps message types with "handlers" that
7//! can be called with them.
8//!
9//! The focal point of this crate is the `HandlerMap` type, which stores information about
10//! functions which receive various types. This can be used to encode event handlers, message
11//! handlers, or other situations where you want to dynamically select a function to call based on
12//! the data it receives.
13//!
14//! To register a handler, pass it to `insert`:
15//!
16//! ```rust
17//! use handler_map::HandlerMap;
18//!
19//! /// Message which prints to the console when received.
20//! struct MyMessage;
21//!
22//! fn handle(_: MyMessage) {
23//! println!("got your message!");
24//! }
25//!
26//! let mut map = HandlerMap::new();
27//! map.insert(handle);
28//! ```
29//!
30//! This adds the handler to the map so that it can be `call`ed later on:
31//!
32//! ```rust
33//! # use handler_map::HandlerMap;
34//!
35//! # /// Message which prints to the console when received.
36//! # struct MyMessage;
37//!
38//! # fn handle(_: MyMessage) {
39//! # println!("got your message!");
40//! # }
41//!
42//! # let mut map = HandlerMap::new();
43//! # map.insert(handle);
44//! map.call(MyMessage);
45//! ```
46//!
47//! The map can also take closures, as long as they implement `Fn` and don't capture any references
48//! to their environment:
49//!
50//! ```rust
51//! use handler_map::HandlerMap;
52//! use std::rc::Rc;
53//! use std::cell::Cell;
54//!
55//! /// Message which increments an accumulator when received.
56//! struct MyMessage;
57//!
58//! let mut map = HandlerMap::new();
59//! let acc = Rc::new(Cell::new(0));
60//! {
61//! let acc = acc.clone();
62//! map.insert(move |_: MyMessage| {
63//! acc.set(acc.get() + 1);
64//! });
65//! }
66//!
67//! // call the handler a few times to increment the counter
68//! map.call(MyMessage);
69//! map.call(MyMessage);
70//! map.call(MyMessage);
71//!
72//! assert_eq!(acc.get(), 3);
73//! ```
74//!
75//! `call` can take a message of any type, even if that type hasn't been registered. It returns a
76//! `bool` representing whether a handler was called. If a handler for that type has been
77//! registered in the map, it returns `true`; otherwise, it returns `false`. If you want to check
78//! that a handler has been registered without calling it, use `is_registered` or
79//! `val_is_registered`.
80//!
81//! If you want to remove an event from the handler, call `remove`:
82//!
83//! ```rust
84//! use handler_map::HandlerMap;
85//!
86//! struct MyMessage;
87//! fn handle_msg(_: MyMessage) {}
88//!
89//! let mut map = HandlerMap::new();
90//! map.insert(handle_msg);
91//!
92//! assert!(map.is_registered::<MyMessage>());
93//!
94//! map.remove::<MyMessage>();
95//!
96//! assert!(!map.is_registered::<MyMessage>());
97//! ```
98
99mod boxfn;
100
101use std::any::{Any, TypeId};
102use std::collections::HashMap;
103
104use boxfn::{BoxFn, Opaque};
105
106/// Struct that maps types with functions or closures that can receive them.
107///
108/// See the [module-level documentation](index.html) for more information.
109#[derive(Default)]
110pub struct HandlerMap(HashMap<TypeId, BoxFn<'static, Opaque>>);
111
112impl HandlerMap {
113 /// Creates a new map with no handlers.
114 pub fn new() -> HandlerMap {
115 Self::default()
116 }
117
118 /// Registers a new handler into the map.
119 pub fn insert<T: Any, F: Fn(T) + 'static>(&mut self, handler: F) {
120 let ptr: BoxFn<'static, T, F> = Box::new(handler).into();
121 let ptr: BoxFn<'static, Opaque> = ptr.erase().erase_arg();
122 let id = TypeId::of::<T>();
123
124 self.0.insert(id, ptr);
125 }
126
127 /// Un-registers the handler for the given type from this map.
128 pub fn remove<T: Any>(&mut self) {
129 let id = TypeId::of::<T>();
130 self.0.remove(&id);
131 }
132
133 /// Returns true if the given message type has a handler registered in the map.
134 pub fn is_registered<T: Any>(&self) -> bool {
135 let id = TypeId::of::<T>();
136 self.0.contains_key(&id)
137 }
138
139 /// Returns true if the given message has a handler registered in this map.
140 ///
141 /// This is the same operation as `is_registered`, but allows you to call it with a value
142 /// rather than having to supply the type.
143 pub fn val_is_registered<T: Any>(&self, _msg: &T) -> bool {
144 self.is_registered::<T>()
145 }
146
147 /// Calls the handler with the given message, returning whether the handler was registered.
148 pub fn call<T: Any>(&self, msg: T) -> bool {
149 let id = TypeId::of::<T>();
150 if let Some(act) = self.0.get(&id) {
151 unsafe { act.call_erased(msg); }
152 true
153 } else {
154 false
155 }
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::HandlerMap;
162
163 #[test]
164 fn it_works() {
165 struct MyMessage;
166 fn respond(_: MyMessage) {}
167
168 let mut map = HandlerMap::new();
169 map.insert(respond);
170
171 assert!(map.call(MyMessage));
172 }
173
174 #[test]
175 fn no_handler() {
176 struct MyMessage;
177
178 let map = HandlerMap::new();
179
180 assert!(!map.call(MyMessage));
181 }
182
183 #[test]
184 fn handler_is_called() {
185 use std::sync::Arc;
186 use std::sync::atomic::AtomicUsize;
187 use std::sync::atomic::Ordering::SeqCst;
188
189 let mut map = HandlerMap::new();
190
191 struct FancyCaller;
192 let acc = Arc::new(AtomicUsize::new(0));
193 {
194 let acc = acc.clone();
195 map.insert(move |_: FancyCaller| {
196 acc.fetch_add(1, SeqCst);
197 });
198 }
199
200 map.call(FancyCaller);
201 map.call(FancyCaller);
202 map.call(FancyCaller);
203
204 assert_eq!(acc.load(SeqCst), 3);
205 }
206}