use super::{Message, Msg, MsgData}; use std::{ fmt, sync::mpsc::{channel, Receiver, Sender}, thread::spawn, }; use uuid::Uuid; #[derive(Clone)] pub enum SessionMsg { Get(Option), Opened(SessionData), } impl Msg for SessionMsg { fn to_msgdata(&self) -> MsgData { MsgData::Session(self.clone()) } } #[derive(Clone)] pub struct SessionData { id: Uuid, } impl SessionData { pub fn new(id: Uuid) -> Self { Self { id: id } } } impl fmt::Display for SessionData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.id) } } pub struct Session { router_tx: Sender, session_rx: Receiver, ids: Vec, } impl Session { fn new(router_tx: Sender, session_rx: Receiver) -> Self { Self { router_tx: router_tx, session_rx: session_rx, ids: Vec::new(), } } pub fn start(router_tx: Sender) -> Sender { let (session_tx, session_rx) = channel(); spawn(move || { let mut session = Session::new(router_tx, session_rx); session.listen(); }); session_tx } fn listen(&mut self) { loop { let msg = self.session_rx.recv().unwrap(); match msg.get_message() { MsgData::Session(data) => match data { SessionMsg::Get(req_id) => { let id: Uuid; match req_id { Some(req) => { match Uuid::try_parse(req) { Ok(data) => { if self.ids.contains(&data) { id = data; } else { id = self.create_session(); } }, Err(_) => id = self.create_session(), } }, None => id = self.create_session(), } let data = SessionMsg::Opened(SessionData::new(id)); self.router_tx.send(msg.reply(&data)).unwrap() }, _ => {} }, _ => {} } } } fn create_session(&mut self) -> Uuid { let id = Uuid::new_v4(); self.ids.push(id.clone()); id } } #[cfg(test)] mod messages { use super::{super::test_message::Tester, *}; use std::time::Duration; fn setup_session() -> (Sender, Receiver) { let (tx, rx) = channel(); let session_tx = Session::start(tx); (session_tx, rx) } #[test] fn ignore_unwanted_messages() { let (tx, rx) = setup_session(); let data = Tester::Test1; let msg = Message::new(&data); tx.send(msg).unwrap(); match rx.recv_timeout(Duration::from_millis(500)) { Err(_) => {} _ => unreachable!("Should not receive anything."), } } #[test] fn create_new_session() { let (tx, rx) = setup_session(); let msgdata = SessionMsg::Get(None); let msg = Message::new(&msgdata); tx.send(msg.clone()).unwrap(); let result = rx.recv().unwrap(); assert_eq!(result.get_id(), msg.get_id()); match result.get_message() { MsgData::Session(data) => match data { SessionMsg::Opened(_) => {} _ => unreachable!("Should have been an opened response."), }, _ => unreachable!("Should be a session responsee."), } } #[test] fn ignore_session_replies() { let (tx, rx) = setup_session(); let msgdata = SessionMsg::Opened(SessionData::new(Uuid::new_v4())); let msg = Message::new(&msgdata); tx.send(msg).unwrap(); match rx.recv_timeout(Duration::from_millis(500)) { Err(_) => {} _ => unreachable!("Should not receive anything."), } } #[test] fn ids_must_be_unique() { let (tx, rx) = setup_session(); let msgdata = SessionMsg::Get(None); let mut ids: Vec = Vec::new(); for _ in 0..10 { let msg = Message::new(&msgdata); tx.send(msg).unwrap(); match rx.recv().unwrap().get_message() { MsgData::Session(msg) => match msg { SessionMsg::Opened(sess) => { let id = sess.to_string(); assert!(!ids.contains(&id), "duplicated id found."); ids.push(id); } _ => unreachable!("Shouuld have opened a session."), }, _ => unreachable!("Should be a session message"), } } } #[test] fn expired_ids_get_new() { let (tx, rx) = setup_session(); let old_id = Uuid::new_v4(); let msgdata = SessionMsg::Get(Some(old_id.to_string())); let msg = Message::new(&msgdata); tx.send(msg.clone()).unwrap(); let result = rx.recv().unwrap(); assert_eq!(result.get_id(), msg.get_id()); match result.get_message() { MsgData::Session(msg) => match msg { SessionMsg::Opened(sess) => assert_ne!(sess.to_string(), old_id.to_string()), _ => unreachable!("Should habe been an Opened message."), }, _ => unreachable!("Should have been a session message."), } } #[test] fn bad_session_ids_get_new() { let (tx, rx) = setup_session(); let id = "something badA"; let msgdata = SessionMsg::Get(Some(id.to_string())); let msg = Message::new(&msgdata); tx.send(msg.clone()).unwrap(); let result = rx.recv().unwrap(); assert_eq!(result.get_id(), msg.get_id()); match result.get_message() { MsgData::Session(data) => match data { SessionMsg::Opened(sess) => assert_ne!(sess.to_string(), id), _ => unreachable!("Should habe been an Opened message."), }, _ => unreachable!("Should have been a session message."), } } #[test] fn uses_existing_session() { let (tx, rx) = setup_session(); let msgdata = SessionMsg::Get(None); let msg = Message::new(&msgdata); tx.send(msg).unwrap(); let result = rx.recv().unwrap(); let thesess = match result.get_message() { MsgData::Session(data) => match data { SessionMsg::Opened(sess) => sess, _ => unreachable!("Should habe been an Opened message."), }, _ => unreachable!("Should have been a session message."), }; let msgdata = SessionMsg::Get(Some(thesess.to_string())); let msg = Message::new(&msgdata); tx.send(msg.clone()).unwrap(); let result = rx.recv().unwrap(); assert_eq!(result.get_id(), msg.get_id()); match result.get_message() { MsgData::Session(data) => match data { SessionMsg::Opened(sess) => assert_eq!(sess.to_string(), thesess.to_string(), "Should use existing sesssion."), _ => unreachable!("Should habe been an Opened message."), }, _ => unreachable!("Should have been a session message."), } } }