use std::path::Path;
use toolu_orm_connection::DbConnection;
use toolu_orm_core::dialect::Dialect;
use toolu_orm_core::journal::{Journal, JournalEntry};
use super::error::MigrateError;
use super::store::{ensure_migrations_table, get_applied_migrations, record_migration};
use super::transaction::{begin, commit, rollback_after};
pub async fn mark_applied(
conn: &impl DbConnection,
migrations_dir: &str,
names: &[&str],
dialect: Dialect,
) -> Result<u32, MigrateError> {
let journal = read_journal(migrations_dir)?;
let unknown: Vec<&str> = names
.iter()
.copied()
.filter(|name| !journal.entries.iter().any(|entry| entry.name == *name))
.collect();
if !unknown.is_empty() {
return Err(MigrateError::NotInJournal(unknown.join(", ")));
}
let selected: Vec<&JournalEntry> = journal
.entries
.iter()
.filter(|entry| names.contains(&entry.name.as_str()))
.collect();
record_all(conn, &selected, dialect).await
}
pub async fn mark_applied_through(
conn: &impl DbConnection,
migrations_dir: &str,
last_name: &str,
dialect: Dialect,
) -> Result<u32, MigrateError> {
let journal = read_journal(migrations_dir)?;
let position = journal
.entries
.iter()
.position(|entry| entry.name == last_name)
.ok_or_else(|| MigrateError::NotInJournal(last_name.to_owned()))?;
let selected: Vec<&JournalEntry> = journal.entries.iter().take(position + 1).collect();
record_all(conn, &selected, dialect).await
}
fn read_journal(migrations_dir: &str) -> Result<Journal, MigrateError> {
let journal_path = Path::new(migrations_dir).join("_journal.json");
let journal_path_str = journal_path.to_str().ok_or_else(|| {
MigrateError::ReadFile(format!("{} is not valid UTF-8", journal_path.display()))
})?;
Journal::read_from_path(journal_path_str).map_err(|e| MigrateError::ReadFile(format!("{e}")))
}
async fn record_all(
conn: &impl DbConnection,
entries: &[&JournalEntry],
dialect: Dialect,
) -> Result<u32, MigrateError> {
ensure_migrations_table(conn, dialect).await?;
begin(conn).await?;
let mut count: u32 = 0;
let result = async {
let applied = get_applied_migrations(conn).await?;
for entry in entries {
if applied.contains(&entry.name) {
continue;
}
record_migration(conn, &entry.name, &entry.hash, dialect).await?;
count += 1;
}
Ok(())
}
.await;
if let Err(e) = result {
return Err(rollback_after(conn, e).await);
}
commit(conn).await?;
Ok(count)
}