use crate::{client::*, ASAP, FOREVER};
use log::*;
use std::{collections::HashMap,
time::{Duration, Instant}};
use tokio::sync::oneshot;
pub(crate) struct Sessions(HashMap<String, Session>);
impl Sessions {
pub fn new() -> Self {
Self(HashMap::new())
}
pub async fn open(&mut self, client: &Client<'_>, clean_session: bool) -> SessionData {
trace!("C{}: load session {} {:?}", client.id, client.name, self.0.keys());
let prev_session = match self.0.remove(&client.name) {
Some(s) => s.aquire(client).await,
None => None,
};
self.0
.insert(client.name.clone(), Session::new(client, &client.sess_expire, clean_session));
match prev_session {
Some(_) if !clean_session => prev_session.unwrap(),
_ => SessionData::default(),
}
}
pub fn close(&mut self, client: &Client, session: SessionData) {
match self.0.get(&client.name) {
None => panic!("C{}: Closing non-existing session", client.id),
Some(Session::Closed(_, _)) => panic!("C{}: Closing already-closed session", client.id),
Some(Session::Live(addr, d)) => {
assert_eq!(*addr, client.addr, "C{}: closing another client's session", client.id);
if *d > ASAP {
debug!("C{}: close session {} {:?}, expire in {:?}",
client.id, client.name, session, *d);
let expire = Instant::now() + *d;
self.0.insert(client.name.clone(), Session::Closed(session, expire));
} else {
debug!("C{}: remove session {}", client.id, client.name);
self.0.remove(&client.name);
}
},
}
}
}
pub(crate) enum Session {
Live(Addr, Duration),
Closed(SessionData, Instant),
}
impl Session {
fn new(client: &Client, expire: &Option<Duration>, clean_session: bool) -> Self {
Self::Live(client.addr.clone(), match expire {
None if clean_session => ASAP,
None => FOREVER,
Some(d) => *d,
})
}
async fn aquire(self, client: &Client<'_>) -> Option<SessionData> {
match self {
Self::Live(addr, d) => {
debug!("C{}: aquiring session from C{} {:?}", client.id, addr.1, d);
let (snd, rcv) = oneshot::channel();
addr.send(Msg::Replaced(client.id, snd)).await;
if d > ASAP {
Some(rcv.await.unwrap())
} else {
None
}
},
Self::Closed(data, date) => {
if date > Instant::now() {
debug!("C{}: reopening session", client.id);
Some(data)
} else {
debug!("C{}: droping closed session", client.id);
None
}
},
}
}
}