doot/crates/doot-core/src/state/snapshot.rs
2026-02-05 20:49:22 -06:00

87 lines
2.5 KiB
Rust

//! 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<Self, StateError> {
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<Self, StateError> {
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<Vec<String>, 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)
}