Skip to main content

git_same/cache/
sync_history.rs

1use anyhow::{Context, Result};
2use serde::{Deserialize, Serialize};
3use std::fs;
4use std::path::{Path, PathBuf};
5use tracing::debug;
6
7use crate::tui::app::SyncHistoryEntry;
8
9const HISTORY_VERSION: u32 = 1;
10const MAX_HISTORY_ENTRIES: usize = 50;
11
12#[derive(Debug, Serialize, Deserialize)]
13struct SyncHistoryFile {
14    version: u32,
15    entries: Vec<SyncHistoryEntry>,
16}
17
18/// Manages per-workspace sync history persistence.
19///
20/// History is stored at `<workspace-root>/.git-same/sync-history.json`.
21pub struct SyncHistoryManager {
22    path: PathBuf,
23}
24
25impl SyncHistoryManager {
26    /// Create a history manager for a specific workspace root path.
27    pub fn for_workspace(root: &Path) -> Result<Self> {
28        let path = crate::config::WorkspaceStore::sync_history_path(root);
29        Ok(Self { path })
30    }
31
32    /// Load sync history from disk. Returns empty vec if file doesn't exist.
33    pub fn load(&self) -> Result<Vec<SyncHistoryEntry>> {
34        if !self.path.exists() {
35            return Ok(Vec::new());
36        }
37        let content = fs::read_to_string(&self.path).context("Failed to read sync history file")?;
38        let file: SyncHistoryFile =
39            serde_json::from_str(&content).context("Failed to parse sync history")?;
40        if file.version != HISTORY_VERSION {
41            debug!(
42                file_version = file.version,
43                current_version = HISTORY_VERSION,
44                "Sync history version mismatch, starting fresh"
45            );
46            return Ok(Vec::new());
47        }
48        Ok(file.entries)
49    }
50
51    /// Save sync history to disk, keeping only the most recent entries.
52    pub fn save(&self, entries: &[SyncHistoryEntry]) -> Result<()> {
53        if let Some(parent) = self.path.parent() {
54            fs::create_dir_all(parent).context("Failed to create history directory")?;
55        }
56        let capped: Vec<SyncHistoryEntry> = entries
57            .iter()
58            .rev()
59            .take(MAX_HISTORY_ENTRIES)
60            .rev()
61            .cloned()
62            .collect();
63        let file = SyncHistoryFile {
64            version: HISTORY_VERSION,
65            entries: capped,
66        };
67        let json =
68            serde_json::to_string_pretty(&file).context("Failed to serialize sync history")?;
69        fs::write(&self.path, &json).context("Failed to write sync history")?;
70        debug!(
71            path = %self.path.display(),
72            entries = file.entries.len(),
73            "Saved sync history"
74        );
75        Ok(())
76    }
77}
78
79#[cfg(test)]
80#[path = "sync_history_tests.rs"]
81mod tests;