75 lines
2.9 KiB
Rust
75 lines
2.9 KiB
Rust
|
|
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<Commands>;
|
|
pub type RecvQ = Receiver<Commands>;
|
|
|
|
//TODO: Making this generic over a <C> for commands would make it a useful small event handler.
|
|
|
|
pub struct Dispatch {
|
|
callbacks: HashMap<CmdType, Vec<SendQ> >,
|
|
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<Commands>) -> 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")}
|
|
}
|
|
}
|
|
}
|
|
} |