use anyhow::Result;
use std::path::Path;
use crate::loader::discover_migrations;
use crate::state::{get_current_version, get_pending, get_target_version, read_history};
pub fn run(project_root: &Path, migrations_dir: &Path) -> Result<()> {
let migrations_path = if migrations_dir.is_absolute() {
migrations_dir.to_path_buf()
} else {
project_root.join(migrations_dir)
};
if !migrations_path.exists() {
println!(
"No migrations directory found at: {}",
migrations_path.display()
);
return Ok(());
}
let available = discover_migrations(&migrations_path)?;
let state = read_history(&migrations_path)?;
let pending = get_pending(&available, &state);
if available.is_empty() && state.baseline.is_none() {
println!("No migrations found in: {}", migrations_path.display());
return Ok(());
}
let current_version = get_current_version(&available, &state.applied);
let target_version = get_target_version(&available);
println!("Migration Status");
println!("================");
println!();
if let Some(ref b) = state.baseline {
println!("Baseline: {} ({})", b.version, b.created.format("%Y-%m-%d"));
if let Some(ref summary) = b.summary {
for line in summary.lines() {
println!(" {}", line);
}
}
println!();
}
match (¤t_version, &target_version) {
(None, Some(target)) if state.baseline.is_some() => {
println!(
"Version: {} -> {} ({} pending)",
state.baseline.as_ref().unwrap().version,
target,
pending.len()
);
}
(None, Some(target)) => {
println!("Version: (none) -> {} ({} pending)", target, pending.len());
}
(Some(current), Some(target)) if current == target => {
println!("Version: {} (up to date)", current);
}
(Some(current), Some(target)) => {
println!(
"Version: {} -> {} ({} pending)",
current,
target,
pending.len()
);
}
(None, None) if state.baseline.is_some() => {
println!(
"Version: {} (up to date, baselined)",
state.baseline.as_ref().unwrap().version
);
}
_ => {}
}
println!();
if !state.applied.is_empty() {
println!("Applied ({}):", state.applied.len());
for migration in &state.applied {
let is_baselined = state
.baseline
.as_ref()
.is_some_and(|b| extract_version(&migration.id) <= Some(b.version.clone()));
if is_baselined {
println!(
" + {} {} (baseline)",
migration.id,
migration.applied_at.format("%Y-%m-%d %H:%M:%S")
);
} else {
println!(
" + {} {}",
migration.id,
migration.applied_at.format("%Y-%m-%d %H:%M:%S")
);
}
}
println!();
}
if !pending.is_empty() {
println!("Pending ({}):", pending.len());
for migration in &pending {
println!(" - {}", migration.id);
}
}
Ok(())
}
fn extract_version(id: &str) -> Option<String> {
if id.len() >= 5 && id.chars().nth(5) == Some('-') {
Some(id[..5].to_string())
} else {
None
}
}