use std::mem::discriminant; use std::collections::HashMap; use std::vec::Vec; use async_channel::{unbounded, Receiver, Sender}; use strum::EnumCount; use crate::commands::{Commands, CmdType}; use log::*; //{trace, debug, info, warn, error} pub type SendQ = Sender; pub type RecvQ = Receiver; //TODO: Making this generic over a for commands would make it a useful small event handler. pub struct Dispatch { callbacks: HashMap >, recv: RecvQ, // Channel to listen to incomming commands endpoint: SendQ, // Endpoint to clone to hand to other modules, so that they can send commands } impl Dispatch { pub fn new() -> Dispatch { let (s, r) = unbounded(); //This should always be unbounded, because some callbacks have to send_blocking to it, and if the thread blocks, other tasks can't empty the queue! let mut hmap = HashMap::new(); hmap.reserve(Commands::COUNT); Dispatch { callbacks: hmap, recv: r, endpoint: s} } /// Get a channel receiver that will get callbacks for all commands in the listen_for vec. pub fn get_callback_channel(&mut self, listen_for: &Vec) -> RecvQ { let (send, rec) = unbounded(); // TODO: these could be bounded instead, as these calls are all non-blocking. for cmd in listen_for { let callback_list = self.callbacks.get_mut(&discriminant(&cmd)); match callback_list { Some(callback) => { callback.push(send.clone()); trace!("Adding {:?} to callbacks", cmd); } None => { let mut callback = Vec::new(); callback.push(send.clone()); self.callbacks.insert(discriminant(&cmd), callback); trace!("Created {:?} callback", cmd); } } } rec } /// Get a channel sender that will send commands to this dispatcher pub fn get_cmd_channel(&self) -> SendQ { self.endpoint.clone() } /// Wait on incomming commands and dispatch them pub async fn cmd_loop(&self) -> anyhow::Result<()> { loop { debug!("Dispatch waiting on commands"); let cmd = self.recv.recv().await.expect("Incoming event queue failed unexpectedly"); debug!("Dispatch got command {:?}", cmd); let cmd_type = discriminant(&cmd); let found_listeners = self.callbacks.get(&cmd_type); match found_listeners { Some(listeners) => { for listener in listeners { trace!("Sending cmd {:?}", cmd); listener.send(cmd.clone()).await.expect("Outgoing event queue failed unexpectedly"); } } None => {debug!("Dispatch found no listeners for a command")} } } } }