cascade_cli/stack/
sync_state.rs

1use crate::errors::{CascadeError, Result};
2use serde::{Deserialize, Serialize};
3use std::path::Path;
4
5/// State for an in-progress sync operation
6#[derive(Serialize, Deserialize, Debug, Clone)]
7pub struct SyncState {
8    /// ID of the stack being synced
9    pub stack_id: String,
10    /// Name of the stack
11    pub stack_name: String,
12    /// Branch user was on before sync started
13    pub original_branch: String,
14    /// Base branch being rebased onto
15    pub target_base: String,
16    /// Entry IDs that still need to be processed
17    pub remaining_entry_ids: Vec<String>,
18    /// Entry currently being cherry-picked (the one with conflicts)
19    pub current_entry_id: String,
20    /// Current entry's branch name
21    pub current_entry_branch: String,
22    /// Temp branch for current entry
23    pub current_temp_branch: String,
24    /// All temp branches created so far (for cleanup)
25    pub temp_branches: Vec<String>,
26}
27
28impl SyncState {
29    /// Save sync state to disk
30    pub fn save(&self, repo_root: &Path) -> Result<()> {
31        let state_path = repo_root.join(".git").join("CASCADE_SYNC_STATE");
32        let json = serde_json::to_string_pretty(self)
33            .map_err(|e| CascadeError::config(format!("Failed to serialize sync state: {e}")))?;
34
35        std::fs::write(&state_path, json)
36            .map_err(|e| CascadeError::config(format!("Failed to write sync state: {e}")))?;
37
38        tracing::debug!("Saved sync state to {:?}", state_path);
39        Ok(())
40    }
41
42    /// Load sync state from disk
43    pub fn load(repo_root: &Path) -> Result<Self> {
44        let state_path = repo_root.join(".git").join("CASCADE_SYNC_STATE");
45
46        if !state_path.exists() {
47            return Err(CascadeError::config(
48                "No in-progress sync found. Nothing to continue.".to_string(),
49            ));
50        }
51
52        let json = std::fs::read_to_string(&state_path)
53            .map_err(|e| CascadeError::config(format!("Failed to read sync state: {e}")))?;
54
55        let state: Self = serde_json::from_str(&json)
56            .map_err(|e| CascadeError::config(format!("Failed to parse sync state: {e}")))?;
57
58        tracing::debug!("Loaded sync state from {:?}", state_path);
59        Ok(state)
60    }
61
62    /// Delete sync state file
63    pub fn delete(repo_root: &Path) -> Result<()> {
64        let state_path = repo_root.join(".git").join("CASCADE_SYNC_STATE");
65
66        if state_path.exists() {
67            std::fs::remove_file(&state_path)
68                .map_err(|e| CascadeError::config(format!("Failed to delete sync state: {e}")))?;
69            tracing::debug!("Deleted sync state file");
70        }
71
72        Ok(())
73    }
74
75    /// Check if sync state exists
76    pub fn exists(repo_root: &Path) -> bool {
77        repo_root.join(".git").join("CASCADE_SYNC_STATE").exists()
78    }
79}