use okerr::Result;
use crate::BoxMigration;
use crate::Metadata;
pub struct Migratex<'m, 'c, MigContext, M: Metadata> {
ctx: &'c mut MigContext,
meta: &'m mut M,
migrations: Vec<BoxMigration<MigContext>>,
}
impl<'m, 'c, MigContext, M: Metadata> Migratex<'m, 'c, MigContext, M> {
pub fn new(
ctx: &'c mut MigContext,
meta: &'m mut M,
migrations: Vec<BoxMigration<MigContext>>,
) -> Self {
Self {
ctx,
meta,
migrations,
}
}
pub fn metadata(&self) -> &M {
self.meta
}
pub fn context(&self) -> &MigContext {
self.ctx
}
pub fn latest_version(&self) -> i32 {
self.migrations.last().map(|m| m.version()).unwrap_or(0)
}
pub async fn migrate_to_latest(&mut self) -> Result<()> {
let target = self.latest_version();
self.migrate_to(target).await
}
pub async fn migrate_to_zero(&mut self) -> Result<()> {
self.migrate_to(0).await
}
pub async fn migrate_next(&mut self) -> Result<()> {
let current = self.meta.version();
if current >= self.latest_version() {
return Ok(());
}
self.migrate_to(current + 1).await
}
pub async fn migrate_prev(&mut self) -> Result<()> {
let current = self.meta.version();
if current <= 0 {
return Ok(());
}
self.migrate_to(current - 1).await
}
pub async fn migrate_to(&mut self, target: i32) -> Result<()> {
let current = self.meta.version();
if target == current {
return Ok(());
}
self.meta.mark_migrating();
let result = if target > current {
self.migrate_up(current, target).await
} else {
self.migrate_down(current, target).await
};
match result {
Ok(()) => {
self.meta.mark_clean();
Ok(())
}
Err(e) => {
self.meta.mark_failed();
Err(e)
}
}
}
async fn migrate_up(&mut self, current: i32, target: i32) -> Result<()> {
for m in &self.migrations {
let v = m.version();
if v > current && v <= target {
m.up(self.ctx).await?;
self.meta.set_version(v);
}
}
Ok(())
}
async fn migrate_down(&mut self, current: i32, target: i32) -> Result<()> {
let mut sorted: Vec<_> = self.migrations.iter().collect();
sorted.sort_by_key(|m| m.version());
sorted.reverse();
for m in sorted {
let v = m.version();
if v <= current && v > target {
m.down(self.ctx).await?;
self.meta.set_version(v - 1);
}
}
Ok(())
}
}