use super::{ActionOutcome, SkippedCopy, UndoReport};
use crate::paths::ShelfPaths;
use crate::registry::{LocationRole, RegistryStore};
use crate::{Error, Result};
pub(crate) fn undo(paths: &ShelfPaths, store: &RegistryStore, op_id: &str) -> Result<UndoReport> {
let mut journal = super::execute::read_journal(paths, op_id)?;
let mut report = UndoReport::default();
for ja in &mut journal.actions {
if ja.outcome != ActionOutcome::Applied {
continue;
}
let action = &ja.action;
let result = (|| -> Result<()> {
if !action.blob.is_file() {
return Err(Error::Refused(
"store blob no longer exists; cannot re-materialize".into(),
));
}
if !action.target.exists() {
return Err(Error::Refused("target no longer exists".into()));
}
let tmp = super::execute::swap_tmp_path(&action.target);
let _ = std::fs::remove_file(&tmp);
std::fs::copy(&action.blob, &tmp).map_err(|e| Error::io(&tmp, e))?;
if let Err(e) = std::fs::rename(&tmp, &action.target) {
let _ = std::fs::remove_file(&tmp);
return Err(Error::io(&action.target, e));
}
Ok(())
})();
match result {
Ok(()) => {
ja.outcome = ActionOutcome::Undone;
ja.reason = None;
report.restored += 1;
let target = action.target.clone();
let id = action.id.clone();
let _ = store.update(|reg| {
if let Some(entry) = reg.find_mut(&id) {
if let Some(loc) = entry.locations.iter_mut().find(|l| l.path == target) {
loc.role = LocationRole::External;
}
}
Ok(())
});
}
Err(e) => report.skipped.push(SkippedCopy {
path: action.target.clone(),
reason: e.to_string(),
}),
}
}
super::execute::write_journal(paths, &journal)?;
Ok(report)
}