use crate::{ queue::{Message, MsgType, Queue}, utils::GenID, }; use std::{ collections::HashMap, sync::{ mpsc::{channel, Receiver, Sender}, Arc, Mutex, }, thread::spawn, }; use uuid::Uuid; pub struct Request; impl Request { pub fn new() -> Self { Self {} } } #[cfg(test)] mod requests { use super::*; pub fn create_request() -> Request { Request::new() } } pub struct Reply; impl Reply { pub fn get_session(&self) -> String { "id".to_string() } pub fn get_content(&self) -> String { "Something goes here.".to_string() } } #[cfg(test)] mod replies { use super::*; pub fn create_reply() -> Reply { Reply {} } } #[derive(Clone)] pub struct ClientRegistry { registry: Arc>>>, } impl ClientRegistry { pub fn new() -> Self { Self { registry: Arc::new(Mutex::new(HashMap::new())), } } fn get_id<'a>( gen: &mut impl Iterator, data: &HashMap>, ) -> Uuid { let mut id = gen.next().unwrap(); while data.contains_key(&id) { id = gen.next().unwrap(); } id.clone() } pub fn add(&mut self, tx: Sender) -> Uuid { let mut reg = self.registry.lock().unwrap(); let mut gen_id = GenID::new(); let id = ClientRegistry::get_id(&mut gen_id, ®); reg.insert(id.clone(), tx); id } fn send(&mut self, id: &Uuid, msg: Reply) { let mut reg = self.registry.lock().unwrap(); let tx = reg.get(id).unwrap(); tx.send(msg).unwrap(); reg.remove(id).unwrap(); } } #[cfg(test)] mod clientregistries { use super::*; use std::{ sync::mpsc::{channel, Receiver}, time::Duration, }; static TIMEOUT: Duration = Duration::from_millis(500); #[test] fn create_client_registry() { let reg = ClientRegistry::new(); let data = reg.registry.lock().unwrap(); assert!(data.is_empty(), "needs to create an empty hashmap"); } #[test] fn send_from_client() { let mut reg = ClientRegistry::new(); let count = 10; let mut rxs: HashMap> = HashMap::new(); for _ in 0..count { let (tx, rx) = channel::(); let id = reg.add(tx); rxs.insert(id, rx); } assert_eq!(rxs.len(), count, "should have been {} receivers", count); for (id, rx) in rxs.iter() { let msg = Reply {}; reg.send(id, msg); rx.recv_timeout(TIMEOUT).unwrap(); } let data = reg.registry.lock().unwrap(); assert!(data.is_empty(), "should remove sender after sending"); } #[test] fn prevent_duplicates() { let mut reg = ClientRegistry::new(); let (tx, rx) = channel::(); let existing = reg.add(tx); let expected = Uuid::new_v4(); let ids = [existing.clone(), expected.clone()]; let data = reg.registry.lock().unwrap(); let result = ClientRegistry::get_id(&mut ids.into_iter(), &data); assert_eq!(result, expected); } } #[derive(Clone)] pub struct ClientLink { tx: Sender, registry: ClientRegistry, } impl ClientLink { fn new(tx: Sender, registry: ClientRegistry) -> Self { Self { tx: tx, registry: registry, } } pub fn send(&mut self, _req: Request) -> Receiver { let (tx, rx) = channel(); let mut msg = Message::new(MsgType::ClientRequest); let id = self.registry.add(tx); msg.add_data("tx_id", id); self.tx.send(msg).unwrap(); rx } } #[cfg(test)] mod clientlinks { use super::*; use std::time::Duration; static TIMEOUT: Duration = Duration::from_millis(500); #[test] fn create_client_link() { let (tx, rx) = channel(); let mut registry = ClientRegistry::new(); let mut link = ClientLink::new(tx, registry.clone()); let req = Request::new(); let rx_client = link.send(req); let msg = rx.recv_timeout(TIMEOUT).unwrap(); match msg.get_class() { MsgType::ClientRequest => {} _ => unreachable!("should have been a client request"), } match msg.get_data().get("tx_id") { Some(result) => { let id = result.to_uuid().unwrap(); registry.send(&id, Reply {}); rx_client.recv().unwrap(); } None => unreachable!("should have had a seender id"), } } } pub struct Client { registry: ClientRegistry, rx: Receiver, } impl Client { fn new(rx: Receiver) -> Self { Self { registry: ClientRegistry::new(), rx: rx, } } pub fn start(_queue: Queue) -> ClientLink { let (tx, rx) = channel(); let mut client = Client::new(rx); let link = ClientLink::new(tx, client.get_registry()); spawn(move || { client.listen(); }); link } fn listen(&mut self) { loop { let msg = self.rx.recv().unwrap(); let id = msg.get_data().get("tx_id").unwrap().to_uuid().unwrap(); let reply = Reply {}; self.registry.send(&id, reply); } } fn get_registry(&self) -> ClientRegistry { self.registry.clone() } } #[cfg(test)] mod clients { use super::*; use requests::create_request; #[test] fn start_client() { let queue = Queue::new(); let mut link = Client::start(queue.clone()); let req = create_request(); link.send(req); } }