cascade_cli/stack/
sync_state.rs1use crate::errors::{CascadeError, Result};
2use serde::{Deserialize, Serialize};
3use std::path::{Path, PathBuf};
4
5#[derive(Serialize, Deserialize, Debug, Clone)]
7pub struct SyncState {
8 pub stack_id: String,
10 pub stack_name: String,
12 pub original_branch: String,
14 pub target_base: String,
16 pub remaining_entry_ids: Vec<String>,
18 pub current_entry_id: String,
20 pub current_entry_branch: String,
22 pub current_temp_branch: String,
24 pub temp_branches: Vec<String>,
26}
27
28impl SyncState {
29 fn state_path(repo_root: &Path) -> Result<PathBuf> {
30 Ok(crate::git::resolve_git_dir(repo_root)?.join("CASCADE_SYNC_STATE"))
31 }
32
33 pub fn save(&self, repo_root: &Path) -> Result<()> {
35 let state_path = Self::state_path(repo_root)?;
36 let json = serde_json::to_string_pretty(self)
37 .map_err(|e| CascadeError::config(format!("Failed to serialize sync state: {e}")))?;
38
39 std::fs::write(&state_path, json)
40 .map_err(|e| CascadeError::config(format!("Failed to write sync state: {e}")))?;
41
42 tracing::debug!("Saved sync state to {:?}", state_path);
43 Ok(())
44 }
45
46 pub fn load(repo_root: &Path) -> Result<Self> {
48 let state_path = Self::state_path(repo_root)?;
49
50 if !state_path.exists() {
51 return Err(CascadeError::config(
52 "No in-progress sync found. Nothing to continue.".to_string(),
53 ));
54 }
55
56 let json = std::fs::read_to_string(&state_path)
57 .map_err(|e| CascadeError::config(format!("Failed to read sync state: {e}")))?;
58
59 let state: Self = serde_json::from_str(&json)
60 .map_err(|e| CascadeError::config(format!("Failed to parse sync state: {e}")))?;
61
62 tracing::debug!("Loaded sync state from {:?}", state_path);
63 Ok(state)
64 }
65
66 pub fn delete(repo_root: &Path) -> Result<()> {
68 let state_path = Self::state_path(repo_root)?;
69
70 if state_path.exists() {
71 std::fs::remove_file(&state_path)
72 .map_err(|e| CascadeError::config(format!("Failed to delete sync state: {e}")))?;
73 tracing::debug!("Deleted sync state file");
74 }
75
76 Ok(())
77 }
78
79 pub fn exists(repo_root: &Path) -> bool {
81 crate::git::resolve_git_dir(repo_root)
82 .map(|d| d.join("CASCADE_SYNC_STATE").exists())
83 .unwrap_or(false)
84 }
85}