use tokio_postgres::{types, Client};
use tokio_postgres as postgres;
#[derive(Clone)]
pub struct MigrationAction<'a> {
pub code: &'static str,
pub vals: &'a [&'a (dyn types::ToSql + Sync)],
}
impl<'a> MigrationAction<'a> {
pub const fn new(code: &'static str, vals: &'a [&'a (dyn types::ToSql + Sync)]) -> Self {
Self { code, vals }
}
}
#[derive(Clone)]
pub struct Migration<'a> {
pub name: &'a str,
pub actions: &'a [MigrationAction<'a>],
}
#[derive(Clone)]
pub struct Schema<'a> {
pub versions: &'a [Migration<'a>],
}
impl<'a> Schema<'a> {
pub async fn migrate(&self, client: &mut Client) -> Result<(), postgres::Error> {
let transaction = client.transaction().await?;
transaction
.execute("create table if not exists __paaradox_meta__(key varchar(1000) primary key, val varchar(1000))", &[])
.await?;
let version = transaction
.query("select val from __paaradox_meta__ where key='version'", &[])
.await?
.into_iter()
.map(|r| r.get::<usize, String>(0))
.next();
if version.is_none() {
transaction
.execute("insert into __paaradox_meta__(key, val) values('version', '--------------- dummy ----------')", &[])
.await?;
}
let needed_migrations: Vec<Migration<'a>> = if let Some(version) = version {
let mut migration_queue = vec![];
let mut migrate = false;
for migration in self.versions.iter() {
if migrate {
migration_queue.push(migration.clone());
}
if migration.name == version {
migrate = true;
}
}
migration_queue
} else {
self.versions.iter().cloned().collect()
};
for migration in needed_migrations.into_iter() {
for action in migration.actions.iter() {
transaction.execute(action.code, action.vals).await?;
}
transaction.execute(
"update __paaradox_meta__ set val = $1 where key = 'version'",
&[&migration.name],
).await?;
}
transaction.commit().await
}
}