use anyhow::Result;
use serde_json::{Map, Value};
pub type MigrationFn = fn(Map<String, Value>) -> Result<Map<String, Value>>;
pub struct MigrationStep {
pub from: &'static str,
pub to: &'static str,
pub apply: MigrationFn,
}
pub fn registered_steps() -> Vec<MigrationStep> {
vec![MigrationStep {
from: "req-v1",
to: "req-v2",
apply: v1_to_v2,
}]
}
fn v1_to_v2(root: Map<String, Value>) -> Result<Map<String, Value>> {
Ok(root)
}
pub fn walk_chain(
mut root: Map<String, Value>,
detected: &str,
target: &str,
) -> Result<(Map<String, Value>, String)> {
if detected == target {
return Ok((root, target.to_string()));
}
let steps = registered_steps();
let mut current = detected.to_string();
let mut applied: Vec<String> = Vec::new();
loop {
if current == target {
return Ok((root, current));
}
let next = steps.iter().find(|s| s.from == current);
match next {
Some(step) => {
root = (step.apply)(root)?;
applied.push(format!("{} → {}", step.from, step.to));
current = step.to.to_string();
}
None => {
return Err(anyhow::anyhow!(
"no migration path from {} to {} (applied so far: {})",
current,
target,
if applied.is_empty() {
"none".to_string()
} else {
applied.join(", ")
}
));
}
}
}
}