ricecoder_tui/
session_manager.rs1use crate::sessions::{Session, SessionStatus, SessionWidget};
7use anyhow::{anyhow, Result};
8use std::collections::HashMap;
9
10pub struct SessionManager {
12 pub widget: SessionWidget,
14 pub storage: HashMap<String, SessionData>,
16 next_id: u64,
18}
19
20#[derive(Debug, Clone)]
22pub struct SessionData {
23 pub id: String,
25 pub name: String,
27 pub content: String,
29 pub created_at: u64,
31 pub modified_at: u64,
33}
34
35impl SessionManager {
36 pub fn new() -> Self {
38 Self {
39 widget: SessionWidget::new(),
40 storage: HashMap::new(),
41 next_id: 1,
42 }
43 }
44
45 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 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 self.widget.add_session(session);
72
73 Ok(id)
74 }
75
76 pub fn delete_session(&mut self, id: &str) -> Result<()> {
78 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 self.storage.remove(id);
87
88 Ok(())
89 }
90
91 pub fn rename_session(&mut self, id: &str, new_name: String) -> Result<()> {
93 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 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 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 if let Some(session) = self.widget.current_session_mut() {
120 session.set_status(SessionStatus::Active);
121 }
122
123 Ok(())
124 }
125
126 pub fn current_session_id(&self) -> Option<String> {
128 self.widget.current_session().map(|s| s.id.clone())
129 }
130
131 pub fn current_session_name(&self) -> Option<String> {
133 self.widget.current_session().map(|s| s.name.clone())
134 }
135
136 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 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 pub fn get_session_content(&self, id: &str) -> Option<String> {
159 self.storage.get(id).map(|d| d.content.clone())
160 }
161
162 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 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 pub fn session_count(&self) -> usize {
182 self.widget.session_count()
183 }
184
185 pub fn has_sessions(&self) -> bool {
187 self.widget.has_sessions()
188 }
189
190 pub fn clear_all_sessions(&mut self) {
192 self.widget.clear();
193 self.storage.clear();
194 }
195
196 pub fn get_session(&self, id: &str) -> Option<&Session> {
198 self.widget.get_session(id)
199 }
200
201 pub fn get_session_data(&self, id: &str) -> Option<&SessionData> {
203 self.storage.get(id)
204 }
205
206 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 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 pub fn widget(&self) -> &SessionWidget {
228 &self.widget
229 }
230
231 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 assert_eq!(manager.current_session_id(), Some(id2.clone()));
320
321 manager.switch_session(&id1).unwrap();
323 assert_eq!(manager.current_session_id(), Some(id1.clone()));
324
325 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}