Skip to main content

ai_agent/utils/
session_restore.rs

1// Source: /data/home/swei/claudecode/openclaudecode/src/utils/sessionRestore.ts
2//! Session restore utilities - restore sessions from file-based storage
3
4use crate::utils::session_storage;
5use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7
8/// Result of session restore operation
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct SessionRestoreResult {
11    pub session_id: String,
12    pub message_count: usize,
13    pub success: bool,
14    pub error: Option<String>,
15}
16
17/// Extended restore result with transcript data
18#[derive(Debug, Clone)]
19pub struct SessionRestoreData {
20    pub result: SessionRestoreResult,
21    pub transcript: Vec<String>,
22}
23
24/// Attempt to restore a session from storage
25pub fn restore_session(session_id: &str) -> SessionRestoreResult {
26    // Check if session exists
27    if !session_storage::session_exists(session_id) {
28        return SessionRestoreResult {
29            session_id: session_id.to_string(),
30            message_count: 0,
31            success: false,
32            error: Some("Session not found".to_string()),
33        };
34    }
35
36    // Load transcript
37    let messages = session_storage::load_transcript(session_id);
38    let message_count = messages.len();
39
40    if message_count == 0 {
41        return SessionRestoreResult {
42            session_id: session_id.to_string(),
43            message_count: 0,
44            success: false,
45            error: Some("Session transcript is empty".to_string()),
46        };
47    }
48
49    SessionRestoreResult {
50        session_id: session_id.to_string(),
51        message_count,
52        success: true,
53        error: None,
54    }
55}
56
57/// Restore a session and return the transcript data
58pub fn restore_session_with_data(session_id: &str) -> SessionRestoreData {
59    let result = restore_session(session_id);
60    let transcript = if result.success {
61        session_storage::load_transcript(session_id)
62    } else {
63        vec![]
64    };
65
66    SessionRestoreData { result, transcript }
67}
68
69/// Check if a session can be restored
70pub fn can_restore_session(session_id: &str) -> bool {
71    // Check if session data exists and is valid
72    session_storage::session_exists(session_id)
73        && session_storage::is_session_data_valid(session_id)
74}
75
76/// Get the path to a session's transcript file
77pub fn get_session_transcript_path(session_id: &str) -> PathBuf {
78    session_storage::get_transcript_path(session_id)
79}
80
81/// List all restorable sessions
82pub fn list_restorable_sessions() -> Vec<String> {
83    session_storage::list_stored_sessions()
84        .into_iter()
85        .filter(|id| can_restore_session(id))
86        .collect()
87}
88
89/// Get the number of messages in a stored session without fully loading it
90pub fn get_stored_message_count(session_id: &str) -> Option<usize> {
91    if !session_storage::session_exists(session_id) {
92        return None;
93    }
94
95    let transcript = session_storage::load_transcript(session_id);
96    if transcript.is_empty() {
97        None
98    } else {
99        Some(transcript.len())
100    }
101}
102
103/// Attempt to restore the most recent session
104pub fn restore_latest_session() -> Option<SessionRestoreResult> {
105    let sessions = list_restorable_sessions();
106    if sessions.is_empty() {
107        return None;
108    }
109
110    // Return the first (most recently modified) session
111    let latest = sessions.first()?;
112    Some(restore_session(latest))
113}
114
115/// Delete a restored session
116pub fn delete_restored_session(session_id: &str) -> Result<(), String> {
117    if !can_restore_session(session_id) {
118        return Err(format!("Cannot delete session: {}", session_id));
119    }
120
121    session_storage::delete_session_storage(session_id)
122}
123
124/// Get the storage size of a session in bytes
125pub fn get_session_storage_size(session_id: &str) -> u64 {
126    session_storage::get_transcript_size(session_id)
127}
128
129/// Validate that a session's transcript is not corrupted
130pub fn validate_session_integrity(session_id: &str) -> Result<(), String> {
131    if !session_storage::session_exists(session_id) {
132        return Err("Session does not exist".to_string());
133    }
134
135    if !session_storage::is_session_data_valid(session_id) {
136        return Err("Session data is corrupted".to_string());
137    }
138
139    Ok(())
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_restore_nonexistent_session() {
148        let result = restore_session("nonexistent-session");
149        assert!(!result.success);
150        assert_eq!(result.message_count, 0);
151        assert!(result.error.is_some());
152    }
153
154    #[test]
155    fn test_can_restore_nonexistent_session() {
156        assert!(!can_restore_session("nonexistent-session"));
157    }
158
159    #[test]
160    fn test_list_restorable_sessions_empty() {
161        let sessions = list_restorable_sessions();
162        assert!(sessions.is_empty());
163    }
164
165    #[test]
166    fn test_get_stored_message_count_nonexistent() {
167        assert!(get_stored_message_count("nonexistent").is_none());
168    }
169
170    #[test]
171    fn test_restore_latest_no_sessions() {
172        assert!(restore_latest_session().is_none());
173    }
174
175    #[test]
176    fn test_delete_nonexistent_session() {
177        let result = delete_restored_session("nonexistent");
178        assert!(result.is_err());
179    }
180
181    #[test]
182    fn test_validate_nonexistent_session() {
183        let result = validate_session_integrity("nonexistent");
184        assert!(result.is_err());
185    }
186
187    #[test]
188    fn test_get_session_storage_size_nonexistent() {
189        assert_eq!(get_session_storage_size("nonexistent"), 0);
190    }
191}