ricecoder_tui/
session_manager.rs

1//! Session management operations
2//!
3//! This module provides session management operations including creation, deletion,
4//! renaming, and persistence of sessions.
5
6use crate::sessions::{Session, SessionStatus, SessionWidget};
7use anyhow::{anyhow, Result};
8use std::collections::HashMap;
9
10/// Session manager for handling session lifecycle
11pub struct SessionManager {
12    /// Session widget
13    pub widget: SessionWidget,
14    /// Session storage (ID -> Session data)
15    pub storage: HashMap<String, SessionData>,
16    /// Next session ID counter
17    next_id: u64,
18}
19
20/// Session data for persistence
21#[derive(Debug, Clone)]
22pub struct SessionData {
23    /// Session ID
24    pub id: String,
25    /// Session name
26    pub name: String,
27    /// Session content/messages
28    pub content: String,
29    /// Creation timestamp
30    pub created_at: u64,
31    /// Last modified timestamp
32    pub modified_at: u64,
33}
34
35impl SessionManager {
36    /// Create a new session manager
37    pub fn new() -> Self {
38        Self {
39            widget: SessionWidget::new(),
40            storage: HashMap::new(),
41            next_id: 1,
42        }
43    }
44
45    /// Create a new session
46    pub fn create_session(&mut self, name: String) -> Result<String> {
47        let id = format!("session-{}", self.next_id);
48        self.next_id += 1;
49
50        let mut session = Session::new(id.clone(), name.clone());
51        session.set_status(SessionStatus::Active);
52
53        // Store session data
54        let now = std::time::SystemTime::now()
55            .duration_since(std::time::UNIX_EPOCH)
56            .map(|d| d.as_secs())
57            .unwrap_or(0);
58
59        self.storage.insert(
60            id.clone(),
61            SessionData {
62                id: id.clone(),
63                name,
64                content: String::new(),
65                created_at: now,
66                modified_at: now,
67            },
68        );
69
70        // Add to widget
71        self.widget.add_session(session);
72
73        Ok(id)
74    }
75
76    /// Delete a session by ID
77    pub fn delete_session(&mut self, id: &str) -> Result<()> {
78        // Find and remove from widget
79        if let Some(index) = self.widget.find_session_index(id) {
80            self.widget.remove_session(index);
81        } else {
82            return Err(anyhow!("Session not found: {}", id));
83        }
84
85        // Remove from storage
86        self.storage.remove(id);
87
88        Ok(())
89    }
90
91    /// Rename a session
92    pub fn rename_session(&mut self, id: &str, new_name: String) -> Result<()> {
93        // Update in widget
94        if let Some(index) = self.widget.find_session_index(id) {
95            self.widget.rename_session(index, new_name.clone());
96        } else {
97            return Err(anyhow!("Session not found: {}", id));
98        }
99
100        // Update in storage
101        if let Some(data) = self.storage.get_mut(id) {
102            data.name = new_name;
103            data.modified_at = std::time::SystemTime::now()
104                .duration_since(std::time::UNIX_EPOCH)
105                .map(|d| d.as_secs())
106                .unwrap_or(0);
107        }
108
109        Ok(())
110    }
111
112    /// Switch to a session
113    pub fn switch_session(&mut self, id: &str) -> Result<()> {
114        if !self.widget.switch_to_session_by_id(id) {
115            return Err(anyhow!("Session not found: {}", id));
116        }
117
118        // Mark as active
119        if let Some(session) = self.widget.current_session_mut() {
120            session.set_status(SessionStatus::Active);
121        }
122
123        Ok(())
124    }
125
126    /// Get the current session ID
127    pub fn current_session_id(&self) -> Option<String> {
128        self.widget.current_session().map(|s| s.id.clone())
129    }
130
131    /// Get the current session name
132    pub fn current_session_name(&self) -> Option<String> {
133        self.widget.current_session().map(|s| s.name.clone())
134    }
135
136    /// Add a message to the current session
137    pub fn add_message_to_current(&mut self, message: &str) -> Result<()> {
138        if let Some(session) = self.widget.current_session_mut() {
139            session.add_message();
140
141            // Update storage
142            if let Some(data) = self.storage.get_mut(&session.id) {
143                data.content.push_str(message);
144                data.content.push('\n');
145                data.modified_at = std::time::SystemTime::now()
146                    .duration_since(std::time::UNIX_EPOCH)
147                    .map(|d| d.as_secs())
148                    .unwrap_or(0);
149            }
150
151            Ok(())
152        } else {
153            Err(anyhow!("No active session"))
154        }
155    }
156
157    /// Get session content
158    pub fn get_session_content(&self, id: &str) -> Option<String> {
159        self.storage.get(id).map(|d| d.content.clone())
160    }
161
162    /// Get all session IDs
163    pub fn all_session_ids(&self) -> Vec<String> {
164        self.widget
165            .session_ids()
166            .iter()
167            .map(|s| s.to_string())
168            .collect()
169    }
170
171    /// Get all session names
172    pub fn all_session_names(&self) -> Vec<String> {
173        self.widget
174            .session_names()
175            .iter()
176            .map(|s| s.to_string())
177            .collect()
178    }
179
180    /// Get session count
181    pub fn session_count(&self) -> usize {
182        self.widget.session_count()
183    }
184
185    /// Check if there are any sessions
186    pub fn has_sessions(&self) -> bool {
187        self.widget.has_sessions()
188    }
189
190    /// Clear all sessions
191    pub fn clear_all_sessions(&mut self) {
192        self.widget.clear();
193        self.storage.clear();
194    }
195
196    /// Get session by ID
197    pub fn get_session(&self, id: &str) -> Option<&Session> {
198        self.widget.get_session(id)
199    }
200
201    /// Get session data by ID
202    pub fn get_session_data(&self, id: &str) -> Option<&SessionData> {
203        self.storage.get(id)
204    }
205
206    /// Mark session as dirty
207    pub fn mark_session_dirty(&mut self, id: &str) -> Result<()> {
208        if let Some(session) = self.widget.get_session_mut(id) {
209            session.mark_dirty();
210            Ok(())
211        } else {
212            Err(anyhow!("Session not found: {}", id))
213        }
214    }
215
216    /// Mark session as clean
217    pub fn mark_session_clean(&mut self, id: &str) -> Result<()> {
218        if let Some(session) = self.widget.get_session_mut(id) {
219            session.mark_clean();
220            Ok(())
221        } else {
222            Err(anyhow!("Session not found: {}", id))
223        }
224    }
225
226    /// Get the widget reference
227    pub fn widget(&self) -> &SessionWidget {
228        &self.widget
229    }
230
231    /// Get mutable widget reference
232    pub fn widget_mut(&mut self) -> &mut SessionWidget {
233        &mut self.widget
234    }
235}
236
237impl Default for SessionManager {
238    fn default() -> Self {
239        Self::new()
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use super::*;
246
247    #[test]
248    fn test_create_session() {
249        let mut manager = SessionManager::new();
250
251        let id = manager.create_session("Test Session".to_string()).unwrap();
252
253        assert!(id.starts_with("session-"));
254        assert_eq!(manager.session_count(), 1);
255        assert_eq!(
256            manager.current_session_name(),
257            Some("Test Session".to_string())
258        );
259    }
260
261    #[test]
262    fn test_create_multiple_sessions() {
263        let mut manager = SessionManager::new();
264
265        let id1 = manager.create_session("Session 1".to_string()).unwrap();
266        let id2 = manager.create_session("Session 2".to_string()).unwrap();
267        let id3 = manager.create_session("Session 3".to_string()).unwrap();
268
269        assert_eq!(manager.session_count(), 3);
270        assert_ne!(id1, id2);
271        assert_ne!(id2, id3);
272    }
273
274    #[test]
275    fn test_delete_session() {
276        let mut manager = SessionManager::new();
277
278        let id = manager.create_session("Test Session".to_string()).unwrap();
279        assert_eq!(manager.session_count(), 1);
280
281        manager.delete_session(&id).unwrap();
282        assert_eq!(manager.session_count(), 0);
283    }
284
285    #[test]
286    fn test_delete_nonexistent_session() {
287        let mut manager = SessionManager::new();
288
289        let result = manager.delete_session("nonexistent");
290        assert!(result.is_err());
291    }
292
293    #[test]
294    fn test_rename_session() {
295        let mut manager = SessionManager::new();
296
297        let id = manager.create_session("Old Name".to_string()).unwrap();
298        manager.rename_session(&id, "New Name".to_string()).unwrap();
299
300        assert_eq!(manager.current_session_name(), Some("New Name".to_string()));
301    }
302
303    #[test]
304    fn test_rename_nonexistent_session() {
305        let mut manager = SessionManager::new();
306
307        let result = manager.rename_session("nonexistent", "New Name".to_string());
308        assert!(result.is_err());
309    }
310
311    #[test]
312    fn test_switch_session() {
313        let mut manager = SessionManager::new();
314
315        let id1 = manager.create_session("Session 1".to_string()).unwrap();
316        let id2 = manager.create_session("Session 2".to_string()).unwrap();
317
318        // After creating id2, it should be the current session
319        assert_eq!(manager.current_session_id(), Some(id2.clone()));
320
321        // Switch to id1
322        manager.switch_session(&id1).unwrap();
323        assert_eq!(manager.current_session_id(), Some(id1.clone()));
324
325        // Switch back to id2
326        manager.switch_session(&id2).unwrap();
327        assert_eq!(manager.current_session_id(), Some(id2.clone()));
328    }
329
330    #[test]
331    fn test_switch_nonexistent_session() {
332        let mut manager = SessionManager::new();
333
334        let result = manager.switch_session("nonexistent");
335        assert!(result.is_err());
336    }
337
338    #[test]
339    fn test_add_message_to_current() {
340        let mut manager = SessionManager::new();
341
342        let id = manager.create_session("Test Session".to_string()).unwrap();
343        manager.add_message_to_current("Hello").unwrap();
344
345        let content = manager.get_session_content(&id).unwrap();
346        assert!(content.contains("Hello"));
347    }
348
349    #[test]
350    fn test_add_message_no_session() {
351        let mut manager = SessionManager::new();
352
353        let result = manager.add_message_to_current("Hello");
354        assert!(result.is_err());
355    }
356
357    #[test]
358    fn test_get_all_session_ids() {
359        let mut manager = SessionManager::new();
360
361        let id1 = manager.create_session("Session 1".to_string()).unwrap();
362        let id2 = manager.create_session("Session 2".to_string()).unwrap();
363
364        let ids = manager.all_session_ids();
365        assert_eq!(ids.len(), 2);
366        assert!(ids.contains(&id1));
367        assert!(ids.contains(&id2));
368    }
369
370    #[test]
371    fn test_get_all_session_names() {
372        let mut manager = SessionManager::new();
373
374        manager.create_session("Session 1".to_string()).unwrap();
375        manager.create_session("Session 2".to_string()).unwrap();
376
377        let names = manager.all_session_names();
378        assert_eq!(names.len(), 2);
379        assert!(names.contains(&"Session 1".to_string()));
380        assert!(names.contains(&"Session 2".to_string()));
381    }
382
383    #[test]
384    fn test_mark_session_dirty() {
385        let mut manager = SessionManager::new();
386
387        let id = manager.create_session("Test Session".to_string()).unwrap();
388        manager.mark_session_dirty(&id).unwrap();
389
390        let session = manager.get_session(&id).unwrap();
391        assert!(session.has_changes);
392    }
393
394    #[test]
395    fn test_mark_session_clean() {
396        let mut manager = SessionManager::new();
397
398        let id = manager.create_session("Test Session".to_string()).unwrap();
399        manager.mark_session_dirty(&id).unwrap();
400        manager.mark_session_clean(&id).unwrap();
401
402        let session = manager.get_session(&id).unwrap();
403        assert!(!session.has_changes);
404    }
405
406    #[test]
407    fn test_clear_all_sessions() {
408        let mut manager = SessionManager::new();
409
410        manager.create_session("Session 1".to_string()).unwrap();
411        manager.create_session("Session 2".to_string()).unwrap();
412
413        assert_eq!(manager.session_count(), 2);
414
415        manager.clear_all_sessions();
416        assert_eq!(manager.session_count(), 0);
417    }
418
419    #[test]
420    fn test_session_data_persistence() {
421        let mut manager = SessionManager::new();
422
423        let id = manager.create_session("Test Session".to_string()).unwrap();
424        manager.add_message_to_current("Message 1").unwrap();
425        manager.add_message_to_current("Message 2").unwrap();
426
427        let data = manager.get_session_data(&id).unwrap();
428        assert_eq!(data.name, "Test Session");
429        assert!(data.content.contains("Message 1"));
430        assert!(data.content.contains("Message 2"));
431    }
432}