use std::path::Path;
pub(crate) fn refuse_multi_stack_restore(
state_dir: &Path,
generation: Option<u32>,
) -> Result<(), String> {
let Ok(Some(lock)) = crate::core::state::load_global_lock(state_dir) else {
return Ok(());
};
let dir = std::fs::canonicalize(state_dir).unwrap_or_else(|_| state_dir.to_path_buf());
let target = generation.map_or_else(
|| "the target generation".to_string(),
|n| format!("generation {n}"),
);
crate::core::state::multi_stack_restore_refusal(&lock, &dir.display().to_string(), &target)
.map_or(Ok(()), Err)
}
pub(super) fn copy_state_to_generation(state_dir: &Path, target: &Path) -> Result<(), String> {
let entries =
std::fs::read_dir(state_dir).map_err(|e| format!("cannot read state dir: {e}"))?;
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if name == "generations" || name == "snapshots" || name == ".snapshots" {
continue;
}
let src = entry.path();
let dst = target.join(&name);
if src.is_dir() {
std::fs::create_dir_all(&dst)
.map_err(|e| format!("cannot create {}: {e}", dst.display()))?;
crate::cli::snapshot::copy_dir_recursive(&src, &dst, "")?;
} else {
std::fs::copy(&src, &dst)
.map_err(|e| format!("cannot copy {} → {}: {e}", src.display(), dst.display()))?;
}
}
Ok(())
}
pub(super) fn restore_generation_to_state(gen_path: &Path, state_dir: &Path) -> Result<(), String> {
let entries =
std::fs::read_dir(state_dir).map_err(|e| format!("cannot read state dir: {e}"))?;
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if name == "generations" || name == "snapshots" || name == ".snapshots" {
continue;
}
let path = entry.path();
if path.is_dir() {
std::fs::remove_dir_all(&path)
.map_err(|e| format!("cannot remove {}: {e}", path.display()))?;
} else {
std::fs::remove_file(&path)
.map_err(|e| format!("cannot remove {}: {e}", path.display()))?;
}
}
let entries =
std::fs::read_dir(gen_path).map_err(|e| format!("cannot read generation: {e}"))?;
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if name == ".generation.yaml" || name == crate::cli::undo_replay::APPLIED_CONFIG {
continue;
}
let src = entry.path();
let dst = state_dir.join(&name);
if src.is_dir() {
std::fs::create_dir_all(&dst)
.map_err(|e| format!("cannot create {}: {e}", dst.display()))?;
crate::cli::snapshot::copy_dir_recursive(&src, &dst, "")?;
} else {
std::fs::copy(&src, &dst)
.map_err(|e| format!("cannot copy {} → {}: {e}", src.display(), dst.display()))?;
}
}
Ok(())
}
pub(super) fn atomic_symlink_switch(gen_dir: &Path, target_dir: &Path) -> Result<(), String> {
let current_link = gen_dir.join("current");
let tmp_link = gen_dir.join("current.tmp");
let _ = std::fs::remove_file(&tmp_link);
#[cfg(unix)]
std::os::unix::fs::symlink(target_dir, &tmp_link)
.map_err(|e| format!("cannot create temp symlink: {e}"))?;
#[cfg(not(unix))]
std::fs::write(&tmp_link, target_dir.to_string_lossy().as_bytes())
.map_err(|e| format!("cannot create temp link: {e}"))?;
std::fs::rename(&tmp_link, ¤t_link).map_err(|e| {
format!(
"cannot rename {} → {}: {e}",
tmp_link.display(),
current_link.display(),
)
})?;
Ok(())
}