use crate::types::MigrateAction;
use anyhow::{Context, Result};
use console::{style, Emoji};
use std::process::Command;
pub fn run_migration(action: MigrateAction) -> Result<()> {
let project_dir = std::env::current_dir().context("Failed to get current directory")?;
println!();
println!("{}", style("🗄️ Database Migration").bold());
println!("{}", style("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━").dim());
match action {
MigrateAction::Up => run_migrations_up(&project_dir)?,
MigrateAction::Down => run_migrations_down(&project_dir)?,
MigrateAction::Status => show_migration_status(&project_dir)?,
MigrateAction::Reset => reset_migrations(&project_dir)?,
}
Ok(())
}
fn run_migrations_up(project_dir: &std::path::Path) -> Result<()> {
println!(" {} Running migrations...", style("↑").cyan());
if !is_sqlx_installed() {
println!();
println!("{} {}", style("⚠️").yellow(), style("sqlx-cli not found. Installing...").dim());
install_sqlx()?;
}
let status = Command::new("sqlx")
.args(["migrate", "run"])
.current_dir(project_dir)
.status()
.context("Failed to run migrations")?;
if status.success() {
println!();
println!("{} {}", Emoji("✅", ""), style("Migrations completed successfully!").green());
} else {
anyhow::bail!("Migration failed with exit code: {:?}", status.code());
}
Ok(())
}
fn run_migrations_down(project_dir: &std::path::Path) -> Result<()> {
println!(" {} Rolling back last migration...", style("↓").cyan());
let status = Command::new("sqlx")
.args(["migrate", "revert"])
.current_dir(project_dir)
.status()
.context("Failed to rollback migration")?;
if status.success() {
println!();
println!("{} {}", Emoji("✅", ""), style("Rollback completed successfully!").green());
} else {
anyhow::bail!("Rollback failed with exit code: {:?}", status.code());
}
Ok(())
}
fn show_migration_status(project_dir: &std::path::Path) -> Result<()> {
println!(" {} Checking migration status...", style("📋").cyan());
let status = Command::new("sqlx")
.args(["migrate", "status"])
.current_dir(project_dir)
.status()
.context("Failed to check migration status")?;
if !status.success() {
anyhow::bail!("Migration status check failed");
}
Ok(())
}
fn reset_migrations(project_dir: &std::path::Path) -> Result<()> {
println!(" {} {} This will drop all tables!", style("⚠️").yellow(), style("WARNING").red().bold());
println!();
let status = Command::new("sqlx")
.args(["migrate", "reset"])
.current_dir(project_dir)
.status()
.context("Failed to reset migrations")?;
if status.success() {
println!();
println!("{} {}", Emoji("✅", ""), style("Reset completed successfully!").green());
} else {
anyhow::bail!("Reset failed with exit code: {:?}", status.code());
}
Ok(())
}
fn is_sqlx_installed() -> bool {
Command::new("sqlx")
.arg("--version")
.output()
.map(|output| output.status.success())
.unwrap_or(false)
}
fn install_sqlx() -> Result<()> {
let status = Command::new("cargo")
.args(["install", "sqlx-cli", "--no-default-features", "--features", "postgres"])
.status()
.context("Failed to install sqlx-cli")?;
if !status.success() {
anyhow::bail!("Failed to install sqlx-cli");
}
Ok(())
}