use dashmap::DashMap; use tokio::sync::{mpsc, oneshot}; use uuid::Uuid; use serde::Serialize; use helios_common::protocol::{ClientMessage, ServerMessage}; /// Represents one connected remote client #[derive(Debug, Clone)] pub struct Session { pub id: Uuid, pub label: Option, /// Channel to send commands to the WS handler for this session pub cmd_tx: mpsc::Sender, } /// Serializable view of a session for the REST API #[derive(Debug, Serialize)] pub struct SessionInfo { pub id: Uuid, pub label: Option, } impl From<&Session> for SessionInfo { fn from(s: &Session) -> Self { SessionInfo { id: s.id, label: s.label.clone(), } } } pub struct SessionStore { /// Active sessions by ID sessions: DashMap, /// Pending request callbacks by request_id pending: DashMap>, } impl SessionStore { pub fn new() -> Self { Self { sessions: DashMap::new(), pending: DashMap::new(), } } pub fn insert(&self, session: Session) { self.sessions.insert(session.id, session); } pub fn remove(&self, id: &Uuid) { self.sessions.remove(id); } pub fn get_cmd_tx(&self, id: &Uuid) -> Option> { self.sessions.get(id).map(|s| s.cmd_tx.clone()) } pub fn set_label(&self, id: &Uuid, label: String) -> bool { if let Some(mut s) = self.sessions.get_mut(id) { s.label = Some(label); true } else { false } } pub fn list(&self) -> Vec { self.sessions.iter().map(|e| SessionInfo::from(e.value())).collect() } /// Register a pending request. Returns the receiver to await the client response. pub fn register_pending(&self, request_id: Uuid) -> oneshot::Receiver { let (tx, rx) = oneshot::channel(); self.pending.insert(request_id, tx); rx } /// Deliver a client response to the waiting request handler. /// Returns true if the request was found and resolved. pub fn resolve_pending(&self, request_id: Uuid, msg: ClientMessage) -> bool { if let Some((_, tx)) = self.pending.remove(&request_id) { let _ = tx.send(msg); true } else { false } } }