claude_utils/turbo/
checkpoint_store.rs1use chrono::Utc;
2use std::collections::VecDeque;
3use std::path::PathBuf;
4use tokio::fs;
5use tracing::{error, info};
6
7use super::rollback::Checkpoint;
8
9const CHECKPOINT_FILE: &str = "checkpoints.json";
10
11pub struct CheckpointStore {
12 storage_dir: PathBuf,
13}
14
15impl Default for CheckpointStore {
16 fn default() -> Self {
17 Self::new()
18 }
19}
20
21impl CheckpointStore {
22 pub fn new() -> Self {
23 let storage_dir = dirs::data_dir()
24 .unwrap_or_else(|| PathBuf::from("."))
25 .join("claude-utils")
26 .join("turbo");
27
28 Self { storage_dir }
29 }
30
31 async fn ensure_storage_dir(&self) -> crate::Result<()> {
32 fs::create_dir_all(&self.storage_dir).await?;
33 Ok(())
34 }
35
36 fn checkpoint_file(&self) -> PathBuf {
37 self.storage_dir.join(CHECKPOINT_FILE)
38 }
39
40 pub async fn load_checkpoints(&self) -> crate::Result<VecDeque<Checkpoint>> {
41 self.ensure_storage_dir().await?;
42
43 let file_path = self.checkpoint_file();
44 if !file_path.exists() {
45 return Ok(VecDeque::new());
46 }
47
48 let content = fs::read_to_string(&file_path).await?;
49 let checkpoints: VecDeque<Checkpoint> = serde_json::from_str(&content)
50 .unwrap_or_else(|e| {
51 error!("Failed to parse checkpoints: {}", e);
52 VecDeque::new()
53 });
54
55 Ok(checkpoints)
56 }
57
58 pub async fn save_checkpoints(&self, checkpoints: &VecDeque<Checkpoint>) -> crate::Result<()> {
59 self.ensure_storage_dir().await?;
60
61 let content = serde_json::to_string_pretty(checkpoints)?;
62 fs::write(self.checkpoint_file(), content).await?;
63
64 info!("Saved {} checkpoints to disk", checkpoints.len());
65 Ok(())
66 }
67
68 pub async fn clear_old_checkpoints(&self, max_age_days: i64) -> crate::Result<()> {
69 let mut checkpoints = self.load_checkpoints().await?;
70 let cutoff = Utc::now() - chrono::Duration::days(max_age_days);
71
72 let original_count = checkpoints.len();
73 checkpoints.retain(|cp| cp.timestamp > cutoff);
74
75 if checkpoints.len() < original_count {
76 info!("Removed {} old checkpoints", original_count - checkpoints.len());
77 self.save_checkpoints(&checkpoints).await?;
78 }
79
80 Ok(())
81 }
82}