forge_reasoning/
export_import.rs1use serde::{Deserialize, Serialize};
6
7use crate::checkpoint::{SessionId, TemporalCheckpoint};
8use crate::errors::{Result, StorageError};
9use crate::thread_safe::ThreadSafeStorage;
10
11#[derive(Serialize, Deserialize)]
13pub struct SessionExport {
14 pub version: String,
15 pub session_id: SessionId,
16 pub exported_at: chrono::DateTime<chrono::Utc>,
17 pub checkpoints: Vec<TemporalCheckpoint>,
18}
19
20pub struct CheckpointExporter {
22 storage: ThreadSafeStorage,
23}
24
25impl CheckpointExporter {
26 pub fn new(storage: ThreadSafeStorage) -> Self {
28 Self { storage }
29 }
30
31 pub fn export_session(&self, session_id: &SessionId) -> Result<String> {
33 let summaries = self.storage.list_by_session(*session_id)?;
35
36 let mut checkpoints = Vec::new();
37 for summary in summaries {
38 if let Ok(cp) = self.storage.get(summary.id) {
39 checkpoints.push(cp);
40 }
41 }
42
43 let export = SessionExport {
44 version: "1.0".to_string(),
45 session_id: *session_id,
46 exported_at: chrono::Utc::now(),
47 checkpoints,
48 };
49
50 serde_json::to_string_pretty(&export)
51 .map_err(|e| StorageError::StoreFailed(format!("Export serialization failed: {}", e)).into())
52 }
53
54 pub fn export_session_to_file(&self, session_id: &SessionId, path: &std::path::Path) -> Result<()> {
56 let json = self.export_session(session_id)?;
57 std::fs::write(path, json)
58 .map_err(|e| StorageError::StoreFailed(format!("Failed to write export file: {}", e)).into())
59 }
60}
61
62pub struct CheckpointImporter {
64 storage: ThreadSafeStorage,
65}
66
67impl CheckpointImporter {
68 pub fn new(storage: ThreadSafeStorage) -> Self {
70 Self { storage }
71 }
72
73 pub fn import_session(&self, json: &str) -> Result<usize> {
75 let export: SessionExport = serde_json::from_str(json)
76 .map_err(|e| StorageError::RetrieveFailed(format!("Import deserialization failed: {}", e)))?;
77
78 let mut count = 0;
79 for checkpoint in export.checkpoints {
80 self.storage.store(&checkpoint)?;
81 count += 1;
82 }
83
84 Ok(count)
85 }
86
87 pub fn import_session_from_file(&self, path: &std::path::Path) -> Result<usize> {
89 let json = std::fs::read_to_string(path)
90 .map_err(|e| StorageError::RetrieveFailed(format!("Failed to read import file: {}", e)))?;
91 self.import_session(&json)
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use crate::SqliteGraphStorage;
99
100 #[test]
101 fn test_export_import_roundtrip() {
102 let storage = ThreadSafeStorage::new(SqliteGraphStorage::in_memory().unwrap());
103 let session_id = SessionId::new();
104
105 let exporter = CheckpointExporter::new(storage.clone());
107 let json = exporter.export_session(&session_id).unwrap();
108
109 assert!(json.contains("version"));
111 assert!(json.contains("session_id"));
112
113 let new_storage = ThreadSafeStorage::new(SqliteGraphStorage::in_memory().unwrap());
115 let importer = CheckpointImporter::new(new_storage);
116 let count = importer.import_session(&json).unwrap();
117
118 assert_eq!(count, 0);
120 }
121}