1use crate::config::paths::Paths;
6use crate::session::SessionManager;
7use anyhow::Result;
8use std::fs;
9use std::path::PathBuf;
10use tracing::info;
11
12fn get_archive_dir() -> PathBuf {
14 Paths::data_dir().join("sessions").join("archive")
15}
16
17fn ensure_archive_dir() -> Result<PathBuf> {
19 let dir = get_archive_dir();
20 if !dir.exists() {
21 fs::create_dir_all(&dir)?;
22 }
23 Ok(dir)
24}
25
26pub async fn archive_session(session_id: &str) -> Result<PathBuf> {
37 let archive_dir = ensure_archive_dir()?;
38
39 let json = SessionManager::export_session(session_id).await?;
41
42 let archive_path = archive_dir.join(format!("{}.json", session_id));
44 fs::write(&archive_path, &json)?;
45
46 info!(
47 "Session {} archived to {}",
48 session_id,
49 archive_path.display()
50 );
51
52 Ok(archive_path)
53}
54
55pub async fn archive_and_delete_session(session_id: &str) -> Result<PathBuf> {
62 let archive_path = archive_session(session_id).await?;
63 SessionManager::delete_session(session_id).await?;
64 info!("Session {} deleted after archiving", session_id);
65 Ok(archive_path)
66}
67
68pub async fn bulk_archive_sessions(session_ids: &[String]) -> BulkArchiveResult {
76 let mut result = BulkArchiveResult::default();
77
78 for id in session_ids {
79 match archive_session(id).await {
80 Ok(path) => {
81 result.archived.push((id.clone(), path));
82 }
83 Err(e) => {
84 result.failed.push((id.clone(), e.to_string()));
85 }
86 }
87 }
88
89 result
90}
91
92#[derive(Debug, Default)]
94pub struct BulkArchiveResult {
95 pub archived: Vec<(String, PathBuf)>,
97 pub failed: Vec<(String, String)>,
99}
100
101impl BulkArchiveResult {
102 pub fn all_succeeded(&self) -> bool {
104 self.failed.is_empty()
105 }
106
107 pub fn success_count(&self) -> usize {
109 self.archived.len()
110 }
111
112 pub fn failure_count(&self) -> usize {
114 self.failed.len()
115 }
116}
117
118pub fn list_archived_sessions() -> Result<Vec<String>> {
123 let archive_dir = get_archive_dir();
124
125 if !archive_dir.exists() {
126 return Ok(Vec::new());
127 }
128
129 let mut sessions = Vec::new();
130
131 for entry in fs::read_dir(&archive_dir)? {
132 let entry = entry?;
133 let path = entry.path();
134
135 if path.extension().is_some_and(|ext| ext == "json") {
136 if let Some(stem) = path.file_stem() {
137 sessions.push(stem.to_string_lossy().to_string());
138 }
139 }
140 }
141
142 Ok(sessions)
143}
144
145pub async fn restore_archived_session(session_id: &str) -> Result<crate::session::Session> {
150 let archive_dir = get_archive_dir();
151 let archive_path = archive_dir.join(format!("{}.json", session_id));
152
153 if !archive_path.exists() {
154 anyhow::bail!("Archived session not found: {}", session_id);
155 }
156
157 let json = fs::read_to_string(&archive_path)?;
158 let session = SessionManager::import_session(&json).await?;
159
160 fs::remove_file(&archive_path)?;
162
163 info!("Session {} restored from archive", session_id);
164
165 Ok(session)
166}
167
168pub fn delete_archived_session(session_id: &str) -> Result<()> {
173 let archive_dir = get_archive_dir();
174 let archive_path = archive_dir.join(format!("{}.json", session_id));
175
176 if archive_path.exists() {
177 fs::remove_file(&archive_path)?;
178 info!("Archived session {} deleted", session_id);
179 }
180
181 Ok(())
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 #[test]
189 fn test_bulk_archive_result() {
190 let mut result = BulkArchiveResult::default();
191 assert!(result.all_succeeded());
192 assert_eq!(result.success_count(), 0);
193
194 result
195 .archived
196 .push(("test1".to_string(), PathBuf::from("/tmp/test1.json")));
197 assert!(result.all_succeeded());
198 assert_eq!(result.success_count(), 1);
199
200 result
201 .failed
202 .push(("test2".to_string(), "error".to_string()));
203 assert!(!result.all_succeeded());
204 assert_eq!(result.failure_count(), 1);
205 }
206}