Files
GemBluetoothEmbeddedDevice/gem-remotes-esp32/src/dispatch.rs
2024-08-24 11:49:37 -04:00

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")}
}
}
}
}