Skip to main content

rustant_core/gateway/
session.rs

1//! Gateway session lifecycle management.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use uuid::Uuid;
7
8/// State of a gateway session.
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10pub enum SessionState {
11    Active,
12    Paused,
13    Ended,
14}
15
16/// A gateway session representing an agent interaction.
17#[derive(Debug, Clone)]
18pub struct GatewaySession {
19    pub session_id: Uuid,
20    pub state: SessionState,
21    pub created_at: DateTime<Utc>,
22    pub updated_at: DateTime<Utc>,
23    pub connection_id: Uuid,
24}
25
26/// Manages gateway sessions.
27#[derive(Debug, Default)]
28pub struct SessionManager {
29    sessions: HashMap<Uuid, GatewaySession>,
30}
31
32impl SessionManager {
33    pub fn new() -> Self {
34        Self::default()
35    }
36
37    /// Create a new session for a connection.
38    pub fn create_session(&mut self, connection_id: Uuid) -> Uuid {
39        let now = Utc::now();
40        let session_id = Uuid::new_v4();
41        self.sessions.insert(
42            session_id,
43            GatewaySession {
44                session_id,
45                state: SessionState::Active,
46                created_at: now,
47                updated_at: now,
48                connection_id,
49            },
50        );
51        session_id
52    }
53
54    /// Pause an active session.
55    pub fn pause_session(&mut self, session_id: &Uuid) -> bool {
56        if let Some(session) = self.sessions.get_mut(session_id)
57            && session.state == SessionState::Active
58        {
59            session.state = SessionState::Paused;
60            session.updated_at = Utc::now();
61            return true;
62        }
63        false
64    }
65
66    /// Resume a paused session.
67    pub fn resume_session(&mut self, session_id: &Uuid) -> bool {
68        if let Some(session) = self.sessions.get_mut(session_id)
69            && session.state == SessionState::Paused
70        {
71            session.state = SessionState::Active;
72            session.updated_at = Utc::now();
73            return true;
74        }
75        false
76    }
77
78    /// End a session.
79    pub fn end_session(&mut self, session_id: &Uuid) -> bool {
80        if let Some(session) = self.sessions.get_mut(session_id)
81            && session.state != SessionState::Ended
82        {
83            session.state = SessionState::Ended;
84            session.updated_at = Utc::now();
85            return true;
86        }
87        false
88    }
89
90    /// Get session info.
91    pub fn get(&self, session_id: &Uuid) -> Option<&GatewaySession> {
92        self.sessions.get(session_id)
93    }
94
95    /// Count active sessions.
96    pub fn active_count(&self) -> usize {
97        self.sessions
98            .values()
99            .filter(|s| s.state == SessionState::Active)
100            .count()
101    }
102
103    /// Total sessions (all states).
104    pub fn total_count(&self) -> usize {
105        self.sessions.len()
106    }
107
108    /// List all active sessions.
109    pub fn list_active(&self) -> Vec<&GatewaySession> {
110        self.sessions
111            .values()
112            .filter(|s| s.state == SessionState::Active)
113            .collect()
114    }
115
116    /// Remove ended sessions.
117    pub fn cleanup_ended(&mut self) -> usize {
118        let before = self.sessions.len();
119        self.sessions.retain(|_, s| s.state != SessionState::Ended);
120        before - self.sessions.len()
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn test_create_session() {
130        let mut mgr = SessionManager::new();
131        let conn_id = Uuid::new_v4();
132        let session_id = mgr.create_session(conn_id);
133
134        let session = mgr.get(&session_id).unwrap();
135        assert_eq!(session.state, SessionState::Active);
136        assert_eq!(session.connection_id, conn_id);
137        assert_eq!(mgr.active_count(), 1);
138    }
139
140    #[test]
141    fn test_pause_resume_session() {
142        let mut mgr = SessionManager::new();
143        let session_id = mgr.create_session(Uuid::new_v4());
144
145        assert!(mgr.pause_session(&session_id));
146        assert_eq!(mgr.get(&session_id).unwrap().state, SessionState::Paused);
147        assert_eq!(mgr.active_count(), 0);
148
149        assert!(mgr.resume_session(&session_id));
150        assert_eq!(mgr.get(&session_id).unwrap().state, SessionState::Active);
151        assert_eq!(mgr.active_count(), 1);
152    }
153
154    #[test]
155    fn test_end_session() {
156        let mut mgr = SessionManager::new();
157        let session_id = mgr.create_session(Uuid::new_v4());
158
159        assert!(mgr.end_session(&session_id));
160        assert_eq!(mgr.get(&session_id).unwrap().state, SessionState::Ended);
161        assert_eq!(mgr.active_count(), 0);
162
163        // Can't end an already-ended session
164        assert!(!mgr.end_session(&session_id));
165    }
166
167    #[test]
168    fn test_invalid_state_transitions() {
169        let mut mgr = SessionManager::new();
170        let session_id = mgr.create_session(Uuid::new_v4());
171
172        // Can't resume an active session
173        assert!(!mgr.resume_session(&session_id));
174
175        // Can't pause a paused session
176        mgr.pause_session(&session_id);
177        assert!(!mgr.pause_session(&session_id));
178    }
179
180    #[test]
181    fn test_cleanup_ended() {
182        let mut mgr = SessionManager::new();
183        let s1 = mgr.create_session(Uuid::new_v4());
184        let _s2 = mgr.create_session(Uuid::new_v4());
185        let s3 = mgr.create_session(Uuid::new_v4());
186
187        mgr.end_session(&s1);
188        mgr.end_session(&s3);
189
190        assert_eq!(mgr.total_count(), 3);
191        let removed = mgr.cleanup_ended();
192        assert_eq!(removed, 2);
193        assert_eq!(mgr.total_count(), 1);
194    }
195
196    #[test]
197    fn test_nonexistent_session() {
198        let mut mgr = SessionManager::new();
199        let fake = Uuid::new_v4();
200        assert!(mgr.get(&fake).is_none());
201        assert!(!mgr.pause_session(&fake));
202        assert!(!mgr.resume_session(&fake));
203        assert!(!mgr.end_session(&fake));
204    }
205
206    #[test]
207    fn test_session_state_serialization() {
208        let json = serde_json::to_string(&SessionState::Active).unwrap();
209        let restored: SessionState = serde_json::from_str(&json).unwrap();
210        assert_eq!(restored, SessionState::Active);
211    }
212}