use crate::irx_client::api::ApiKey;
use color_eyre::eyre::Result;
use email_address::EmailAddress;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, hash::Hash};
use tokio::sync::mpsc;
use tracing::{self, instrument};
#[derive(
Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize,
)]
pub enum Payload {
#[default]
Empty,
ApiKey(ApiKey),
Email(EmailAddress),
String(String),
}
#[derive(
Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize,
)]
pub enum Address {
#[default]
Drop,
Router,
IrxClient,
Internals,
StateDisplay,
Session,
Home,
App,
}
#[derive(
Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize,
)]
pub enum Cacheable {
#[default]
No,
Yes,
}
#[derive(
Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize,
)]
pub enum Kind {
#[default]
Tell,
Ask,
}
#[derive(
Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize,
)]
pub struct Message {
pub source: Address,
pub destination: Address,
pub payload: Payload,
pub tag: Option<String>,
pub cacheable: Cacheable,
pub kind: Kind,
}
type ChannelTable = HashMap<Address, mpsc::UnboundedSender<Message>>;
#[derive(Debug)]
pub struct Router {
channel_table: ChannelTable,
message_rx_from_self: Option<mpsc::UnboundedReceiver<Message>>,
}
impl Router {
#[instrument]
pub async fn new(
tx: mpsc::UnboundedSender<Message>,
) -> Result<(Self, mpsc::UnboundedSender<Message>)> {
let (message_tx_to_self, message_rx_from_self) =
mpsc::unbounded_channel::<Message>();
let mut channel_table = ChannelTable::new();
channel_table.insert(Address::App, tx);
channel_table.insert(Address::Router, message_tx_to_self.clone());
Ok((
Self {
channel_table,
message_rx_from_self: Some(message_rx_from_self),
},
message_tx_to_self,
))
}
pub fn register(
&mut self,
addr: Address,
tx: mpsc::UnboundedSender<Message>,
) {
self.channel_table.insert(addr, tx);
}
pub fn run(&mut self) {
let mut message_rx_from_self = self
.message_rx_from_self
.take()
.expect("router has its own receiver"); let channel_table = self.channel_table.clone();
tokio::spawn(async move {
loop {
if let Some(message) = message_rx_from_self.recv().await {
Self::route(message, &channel_table);
}
}
});
}
fn route(message: Message, channel_table: &ChannelTable) {
match channel_table.get(&message.destination) {
Some(tx) => tx.send(message).expect("destination is reachable"),
None => unreachable!(),
}
}
}