use std::path::Path;
use anyhow::Result;
#[derive(Debug, Clone, Copy)]
pub enum MigrationDirection {
Up,
Down,
}
#[derive(Debug, Clone)]
pub struct MigrationResult {
pub applied: usize,
pub reverted: usize,
pub errors: Vec<String>,
}
pub struct Migrator {
migrations_path: String,
}
impl Migrator {
pub fn new(migrations_path: &str) -> Self {
Self {
migrations_path: migrations_path.to_string(),
}
}
pub fn has_migrations(&self) -> bool {
Path::new(&self.migrations_path).exists()
}
pub fn migrations_path(&self) -> &str {
&self.migrations_path
}
pub async fn run(&self) -> Result<MigrationResult> {
use std::process::Command;
let output = Command::new("sqlx")
.args(["migrate", "run"])
.current_dir(&self.migrations_path)
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
let _stdout = String::from_utf8_lossy(&output.stdout);
let mut errors = Vec::new();
if !output.status.success() {
errors.push(stderr.to_string());
}
Ok(MigrationResult {
applied: if output.status.success() { 1 } else { 0 },
reverted: 0,
errors,
})
}
pub async fn revert(&self) -> Result<MigrationResult> {
use std::process::Command;
let output = Command::new("sqlx")
.args(["migrate", "revert"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
let mut errors = Vec::new();
if !output.status.success() {
errors.push(stderr.to_string());
}
Ok(MigrationResult {
applied: 0,
reverted: if output.status.success() { 1 } else { 0 },
errors,
})
}
pub async fn status(&self) -> Result<Vec<MigrationInfo>> {
use std::process::Command;
let output = Command::new("sqlx")
.args(["migrate", "status"])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
let mut migrations = Vec::new();
for line in stdout.lines() {
if line.contains("migration") || line.starts_with('0') || line.starts_with('1') {
migrations.push(MigrationInfo {
version: line.split_whitespace().next().unwrap_or("").to_string(),
name: line.to_string(),
applied: line.starts_with('1'),
});
}
}
Ok(migrations)
}
pub async fn reset(&self) -> Result<MigrationResult> {
use std::process::Command;
let output = Command::new("sqlx")
.args(["migrate", "reset"])
.output()?;
Ok(MigrationResult {
applied: 0,
reverted: if output.status.success() { 1 } else { 0 },
errors: if output.status.success() {
vec![]
} else {
vec![String::from_utf8_lossy(&output.stderr).to_string()]
},
})
}
}
#[derive(Debug, Clone)]
pub struct MigrationInfo {
pub version: String,
pub name: String,
pub applied: bool,
}