From bdb17db5bf4796696d6375168abb76920ab11b87 Mon Sep 17 00:00:00 2001 From: Joscha Date: Mon, 26 Sep 2022 13:20:14 +0200 Subject: [PATCH] Track sessions only known by nick-events --- src/api/types.rs | 2 +- src/conn.rs | 73 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/api/types.rs b/src/api/types.rs index 579b765..b0bdb83 100644 --- a/src/api/types.rs +++ b/src/api/types.rs @@ -394,7 +394,7 @@ impl Time { /// /// It is possible for this value to have no prefix and colon, and there is no /// fixed format for the unique value. -#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct UserId(pub String); impl fmt::Display for UserId { diff --git a/src/conn.rs b/src/conn.rs index 094bb2d..b2b4a12 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -18,8 +18,8 @@ use tokio_tungstenite::{tungstenite, MaybeTlsStream, WebSocketStream}; use crate::api::packet::{Command, Packet, ParsedPacket}; use crate::api::{ - BounceEvent, Data, HelloEvent, LoginReply, PersonalAccountView, Ping, PingReply, SessionId, - SessionView, SnapshotEvent, Time, + BounceEvent, Data, HelloEvent, LoginReply, NickEvent, PersonalAccountView, Ping, PingReply, + SessionId, SessionView, SnapshotEvent, Time, UserId, }; use crate::replies::{self, PendingReply, Replies}; @@ -93,7 +93,7 @@ impl Joining { .listing .iter() .cloned() - .map(|s| (s.session_id.clone(), s)) + .map(|s| (s.session_id.clone(), SessionInfo::Full(s))) .collect::>(); Some(Joined { session, @@ -106,39 +106,86 @@ impl Joining { } } -// TODO Track nick events for listing, add InferredSessionView +#[derive(Debug, Clone)] +pub enum SessionInfo { + Full(SessionView), + Partial(NickEvent), +} + +impl SessionInfo { + pub fn id(&self) -> &UserId { + match self { + Self::Full(sess) => &sess.id, + Self::Partial(nick) => &nick.id, + } + } + + pub fn session_id(&self) -> &SessionId { + match self { + Self::Full(sess) => &sess.session_id, + Self::Partial(nick) => &nick.session_id, + } + } + + pub fn name(&self) -> &str { + match self { + Self::Full(sess) => &sess.name, + Self::Partial(nick) => &nick.to, + } + } +} #[derive(Debug, Clone)] pub struct Joined { pub session: SessionView, pub account: Option, - pub listing: HashMap, + pub listing: HashMap, } impl Joined { fn on_data(&mut self, data: &Data) { match data { Data::JoinEvent(p) => { - self.listing.insert(p.0.session_id.clone(), p.0.clone()); + self.listing + .insert(p.0.session_id.clone(), SessionInfo::Full(p.0.clone())); } Data::SendEvent(p) => { - self.listing - .insert(p.0.sender.session_id.clone(), p.0.sender.clone()); + self.listing.insert( + p.0.sender.session_id.clone(), + SessionInfo::Full(p.0.sender.clone()), + ); } Data::PartEvent(p) => { self.listing.remove(&p.0.session_id); } Data::NetworkEvent(p) => { if p.r#type == "partition" { - self.listing.retain(|_, s| { - !(s.server_id == p.server_id && s.server_era == p.server_era) + self.listing.retain(|_, s| match s { + SessionInfo::Full(s) => { + s.server_id != p.server_id && s.server_era != p.server_era + } + // We can't know if the session was disconnected by the + // partition or not, so we're erring on the side of + // caution and assuming they were kicked. If we're + // wrong, we'll re-add the session as soon as it + // performs another visible action. + // + // If we always kept such sessions, we might keep + // disconnected ones indefinitely, thereby keeping them + // from moving on, instead forever tethering them to the + // digital realm. + SessionInfo::Partial(_) => false, }); } } Data::NickEvent(p) => { - if let Some(session) = self.listing.get_mut(&p.session_id) { - session.name = p.to.clone(); - } + self.listing + .entry(p.session_id.clone()) + .and_modify(|s| match s { + SessionInfo::Full(session) => session.name = p.to.clone(), + SessionInfo::Partial(_) => *s = SessionInfo::Partial(p.clone()), + }) + .or_insert_with(|| SessionInfo::Partial(p.clone())); } Data::NickReply(p) => { assert_eq!(self.session.id, p.id);