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}