use super::*;
pub(crate) fn rewind_event_payload(plan: &RestorePlan, execution: &RewindExecution) -> Value {
let mut counts: BTreeMap<RestoreStatus, usize> = BTreeMap::new();
let paths = execution
.results
.iter()
.map(|result| {
*counts.entry(result.status).or_default() += 1;
let mut item = serde_json::Map::from_iter([
(
"path".to_string(),
json!(redacted_path_for_display(&result.relative_path)),
),
("status".to_string(), json!(result.status)),
]);
if let Some(reason) = &result.reason {
item.insert("reason".to_string(), json!(reason));
}
Value::Object(item)
})
.collect::<Vec<_>>();
serde_json::json!({
"target_turn": plan.target_turn,
"latest_turn": plan.latest_turn,
"paths": paths,
"counts": counts.into_iter().map(|(status, count)| (format!("{status:?}"), count)).collect::<BTreeMap<_, _>>(),
})
}
pub(crate) fn replay_rewind_context(payload: &Value) -> Option<String> {
let target = payload.get("target_turn").and_then(Value::as_u64)?;
let latest = payload
.get("latest_turn")
.and_then(Value::as_u64)
.unwrap_or(target);
let mut lines = vec![format!(
"Local filesystem rewind applied for user turns {target}..{latest}. File contents and diffs omitted."
)];
if let Some(paths) = payload.get("paths").and_then(Value::as_array) {
for item in paths.iter().take(64) {
let path = item
.get("path")
.and_then(Value::as_str)
.unwrap_or("<redacted>");
let status = item
.get("status")
.and_then(Value::as_str)
.unwrap_or("unknown");
lines.push(format!("- {}: {}", safe_provider_path(path), status));
}
}
Some(lines.join("\n"))
}