├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cargo.toml ├── README.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.1.0 2 | 3 | - Add changelog 4 | - Stronger type requirements for traits 5 | - Removed warnings due to rustup 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rdispatcher" 3 | version = "0.1.0" 4 | authors = ["Timon Vonk "] 5 | description = "Dispatcher for Rust, broadcast and subscribe many to many" 6 | homepage = "http://www.github.com/timonv/rdispatcher" 7 | repository = "http://www.github.com/timonv/rdispatcher" 8 | readme = "README.md" 9 | keywords = ["events","dispatcher"] 10 | license = "MIT" 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust Dispatcher 2 | 3 | The Dispatcher allows you to send messages from multiple broadcasters to multiple 4 | receivers. When a lot of messages get passed around a threaded application, centralizing 5 | where messages flow through can create much wanted oversight. 6 | 7 | The types of messages that get passed around are strongly typed, required by 8 | you to setup, and the messages themselves are strings. 9 | 10 | # Install 11 | 12 | Add to your `Cargo.toml` 13 | 14 | ``` 15 | rdispatcher = "*" 16 | ``` 17 | 18 | And run `cargo install` 19 | 20 | # Usage 21 | 22 | A simple example: 23 | 24 | ```rust 25 | let mut dispatcher = Dispatcher::new(); 26 | let sub = TestSubscriber::new(); 27 | let mut brd = TestBroadcaster::new(); 28 | dispatcher.register_broadcaster(&mut brd); 29 | dispatcher.register_subscriber(&sub, OutgoingMessage); 30 | 31 | dispatcher.start(); 32 | 33 | brd.broadcast(OutgoingMessage, "Hello world!".to_string()); 34 | let message = sub.receiver.recv().unwrap(); 35 | assert_eq!(message.dispatch_type, OutgoingMessage); 36 | assert_eq!(message.payload, "Hello world!"); 37 | ``` 38 | 39 | For several full examples, checkout the tests in lib.rs 40 | 41 | # Caveats 42 | 43 | * ~~Currently the DispatchType (I prefer using an enum for it) is cast to a string 44 | using Debug. This is legacy and should just use Hash. If you have a custom Debug 45 | for your your enum, you might get unexpected results.~~ Resolved 46 | * Complex enums (i.e. SomethingComplex(String)) currently do not work as the dispatch 47 | type and will yield weird results. 48 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::sync::{mpsc, Arc, Mutex}; 3 | use std::thread; 4 | use std::hash::{Hash, SipHasher, Hasher}; 5 | use std::clone::Clone; 6 | 7 | // Aliases for easier refactoring 8 | pub type SubscribeHandle = mpsc::Sender>; 9 | pub type BroadcastHandle = mpsc::Receiver>; 10 | 11 | #[derive(Clone)] 12 | pub struct DispatchMessage where T: Hash + Send + Clone { 13 | pub dispatch_type: T, 14 | pub payload: String 15 | } 16 | 17 | pub struct Dispatcher where T: Hash + Send + Clone { 18 | subscribers: HashMap>>, 19 | broadcasters: Vec>>> 20 | } 21 | 22 | pub trait Broadcast { 23 | fn broadcast_handle(&mut self) -> BroadcastHandle; 24 | } 25 | 26 | pub trait Subscribe { 27 | fn subscribe_handle(&self) -> SubscribeHandle; 28 | } 29 | 30 | impl Dispatcher { 31 | pub fn new() -> Dispatcher { 32 | Dispatcher { subscribers: HashMap::new(), broadcasters: vec![] } 33 | } 34 | 35 | pub fn register_broadcaster(&mut self, broadcaster: &mut Broadcast) { 36 | let handle = Arc::new(Mutex::new(broadcaster.broadcast_handle())); 37 | self.broadcasters.push(handle); 38 | } 39 | 40 | pub fn register_subscriber(&mut self, subscriber: &Subscribe, dispatch_type: T) { 41 | let sender = subscriber.subscribe_handle(); 42 | let type_key = type_to_string(&dispatch_type); 43 | let new = match self.subscribers.get_mut(&type_key) { 44 | Some(others) => { 45 | others.push(sender); 46 | None 47 | }, 48 | None => { 49 | Some(vec![sender]) 50 | } 51 | }; 52 | // Improve me. Cant chain because double mut borrow not allowed 53 | new.and_then(|new_senders| self.subscribers.insert(type_key, new_senders)); 54 | } 55 | 56 | pub fn start(&self) { 57 | // Assuming that broadcasters.clone() copies the vector, but increase ref count on children 58 | for broadcaster in self.broadcasters.clone() { 59 | let subscribers = self.subscribers.clone(); 60 | thread::spawn(move || { 61 | loop { 62 | let message = broadcaster.lock().unwrap().recv().ok().expect("Couldn't receive message in broadcaster or channel hung up"); 63 | match subscribers.get(&type_to_string(&message.dispatch_type)) { 64 | Some(ref subs) => { 65 | for sub in subs.iter() { sub.send(message.clone()).unwrap(); } 66 | }, 67 | None => () 68 | } 69 | 70 | } 71 | }); 72 | } 73 | } 74 | 75 | #[allow(dead_code)] 76 | fn num_broadcasters(&self) -> usize { 77 | self.broadcasters.len() 78 | } 79 | 80 | #[allow(dead_code)] 81 | fn num_subscribers(&self, dispatch_type: T) -> usize { 82 | match self.subscribers.get(&type_to_string(&dispatch_type)) { 83 | Some(subscribers) => subscribers.len(), 84 | None => 0 85 | } 86 | } 87 | } 88 | 89 | // Convert to hashable for dispatchtype? 90 | fn type_to_string(t: &T) -> String { 91 | let mut s = SipHasher::new(); 92 | t.hash(&mut s); 93 | s.finish().to_string() 94 | } 95 | 96 | #[cfg(test)] 97 | mod test { 98 | use std::sync::mpsc; 99 | use self::DispatchType::*; 100 | use super::*; 101 | 102 | #[derive(PartialEq, Clone, Hash, Debug)] 103 | pub enum DispatchType { 104 | OutgoingMessage, 105 | RawIncomingMessage 106 | } 107 | 108 | fn setup_dispatcher() -> Dispatcher { 109 | Dispatcher::new() 110 | } 111 | 112 | #[test] 113 | fn test_register_broadcaster() { 114 | let mut dispatcher = setup_dispatcher(); 115 | let mut brd = TestBroadcaster::new(); 116 | assert_eq!(dispatcher.num_broadcasters(), 0); 117 | dispatcher.register_broadcaster(&mut brd); 118 | assert_eq!(dispatcher.num_broadcasters(), 1); 119 | } 120 | 121 | #[test] 122 | fn test_register_subscriber() { 123 | let mut dispatcher = setup_dispatcher(); 124 | let sub = TestSubscriber::new(); 125 | assert_eq!(dispatcher.num_subscribers(OutgoingMessage), 0); 126 | dispatcher.register_subscriber(&sub, OutgoingMessage); 127 | assert_eq!(dispatcher.num_subscribers(OutgoingMessage), 1); 128 | } 129 | 130 | #[test] 131 | fn test_register_multiple_subscribers() { 132 | let mut dispatcher = setup_dispatcher(); 133 | let sub = TestSubscriber::new(); 134 | let sub2 = TestSubscriber::new(); 135 | 136 | assert_eq!(dispatcher.num_subscribers(OutgoingMessage), 0); 137 | dispatcher.register_subscriber(&sub, OutgoingMessage); 138 | dispatcher.register_subscriber(&sub2, OutgoingMessage); 139 | assert_eq!(dispatcher.num_subscribers(OutgoingMessage), 2); 140 | } 141 | 142 | #[test] 143 | fn test_broadcast_simple_message() { 144 | let mut dispatcher = setup_dispatcher(); 145 | let sub = TestSubscriber::new(); 146 | let mut brd = TestBroadcaster::new(); 147 | dispatcher.register_broadcaster(&mut brd); 148 | dispatcher.register_subscriber(&sub, OutgoingMessage); 149 | 150 | dispatcher.start(); 151 | 152 | brd.broadcast(OutgoingMessage, "Hello world!".to_string()); 153 | let message = sub.receiver.recv().unwrap(); 154 | assert_eq!(message.dispatch_type, OutgoingMessage); 155 | assert_eq!(message.payload, "Hello world!"); 156 | } 157 | 158 | #[test] 159 | fn test_broadcast_multiple_to_one() { 160 | let mut dispatcher = setup_dispatcher(); 161 | let sub = TestSubscriber::new(); 162 | let mut brd = TestBroadcaster::new(); 163 | dispatcher.register_broadcaster(&mut brd); 164 | dispatcher.register_subscriber(&sub, OutgoingMessage); 165 | dispatcher.register_subscriber(&sub, RawIncomingMessage); 166 | 167 | dispatcher.start(); 168 | 169 | brd.broadcast(OutgoingMessage, "Hello world!".to_string()); 170 | let message = sub.receiver.recv().unwrap(); 171 | assert_eq!(message.dispatch_type, OutgoingMessage); 172 | assert_eq!(message.payload, "Hello world!"); 173 | brd.broadcast(RawIncomingMessage, "Hello world!".to_string()); 174 | let message = sub.receiver.recv().unwrap(); 175 | assert_eq!(message.dispatch_type, RawIncomingMessage); 176 | assert_eq!(message.payload, "Hello world!"); 177 | } 178 | 179 | // #[test] 180 | // TODO Test is pending, this features needs to be implemented 181 | // or the problem avoided 182 | // 183 | // fn test_broadcast_simple_message_with_complex_enum() { 184 | // let mut dispatcher = setup_dispatcher(); 185 | // let sub = TestSubscriber::new(); 186 | // let mut brd = TestBroadcaster::new(); 187 | // dispatcher.register_broadcaster(&mut brd); 188 | // dispatcher.register_subscriber(&sub, SomethingComplex(String::new())); 189 | 190 | // dispatcher.start(); 191 | 192 | // brd.broadcast(SomethingComplex("abc".to_string()), "Hello world!".to_string()); 193 | // let message = sub.receiver.recv().unwrap(); 194 | // assert_eq!(message.dispatch_type, SomethingComplex("abc".to_string())); 195 | // assert_eq!(message.payload, "Hello world!"); 196 | // } 197 | 198 | struct TestBroadcaster { 199 | sender: Option> 200 | } 201 | 202 | impl TestBroadcaster { 203 | fn new() -> TestBroadcaster { 204 | TestBroadcaster { sender: None } 205 | } 206 | 207 | fn broadcast(&self, dispatch_type: DispatchType, payload: String) { 208 | let message = DispatchMessage { dispatch_type: dispatch_type, payload: payload }; 209 | match self.sender { 210 | Some(ref s) => { s.send(message).unwrap(); }, 211 | None => () 212 | }; 213 | } 214 | } 215 | 216 | impl Broadcast for TestBroadcaster { 217 | fn broadcast_handle(&mut self) -> BroadcastHandle { 218 | let (tx, rx) = mpsc::channel::>(); 219 | self.sender = Some(tx); 220 | rx 221 | } 222 | 223 | } 224 | 225 | struct TestSubscriber { 226 | receiver: BroadcastHandle, 227 | sender: SubscribeHandle 228 | } 229 | 230 | impl TestSubscriber { 231 | fn new() -> TestSubscriber { 232 | let(tx, rx) = mpsc::channel::>(); 233 | TestSubscriber { receiver: rx, sender: tx } 234 | } 235 | } 236 | 237 | impl Subscribe for TestSubscriber { 238 | fn subscribe_handle(&self) -> SubscribeHandle { 239 | self.sender.clone() 240 | } 241 | } 242 | } 243 | --------------------------------------------------------------------------------