//! State snapshots for rollback. use super::store::{State, StateError}; use std::path::Path; /// A named snapshot of doot state. pub struct Snapshot { pub name: String, pub created_at: String, pub state: State, } impl Snapshot { /// Creates and saves a new snapshot. pub fn create(name: &str, state: &State, snapshot_dir: &Path) -> Result { let created_at = chrono_now(); let snapshot = Self { name: name.to_string(), created_at: created_at.clone(), state: state.clone(), }; snapshot.save(snapshot_dir)?; Ok(snapshot) } /// Loads a snapshot by name. pub fn load(name: &str, snapshot_dir: &Path) -> Result { let path = snapshot_dir.join(format!("{}.json", name)); let content = std::fs::read_to_string(&path)?; let state: State = serde_json::from_str(&content)?; Ok(Self { name: name.to_string(), created_at: String::new(), state, }) } /// Saves the snapshot to disk. pub fn save(&self, snapshot_dir: &Path) -> Result<(), StateError> { std::fs::create_dir_all(snapshot_dir)?; let path = snapshot_dir.join(format!("{}.json", self.name)); let json = serde_json::to_string_pretty(&self.state)?; std::fs::write(path, json)?; Ok(()) } /// Lists all snapshots. pub fn list(snapshot_dir: &Path) -> Result, StateError> { if !snapshot_dir.exists() { return Ok(Vec::new()); } let mut snapshots = Vec::new(); for entry in std::fs::read_dir(snapshot_dir)? { let entry = entry?; let path = entry.path(); if path.extension().map(|e| e == "json").unwrap_or(false) { if let Some(name) = path.file_stem() { snapshots.push(name.to_string_lossy().to_string()); } } } snapshots.sort(); Ok(snapshots) } /// Deletes a snapshot. pub fn delete(name: &str, snapshot_dir: &Path) -> Result<(), StateError> { let path = snapshot_dir.join(format!("{}.json", name)); if path.exists() { std::fs::remove_file(path)?; } Ok(()) } } fn chrono_now() -> String { use std::time::{SystemTime, UNIX_EPOCH}; let secs = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); format!("{}", secs) }