use std::sync::Arc; use sqlx::{PgPool, query}; use crate::RegistryError; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ExtensionMigration { pub version: u32, pub sql: &'static str, } pub trait RegistryExtension: Send + Sync { fn name(&self) -> &str; fn migrations(&self) -> &[ExtensionMigration]; } pub async fn apply_extension_migrations( pool: &PgPool, extensions: &[Arc], ) -> Result<(), RegistryError> { query( "create table if not exists __crank_ext_migrations ( extension_name text not null, version integer not null, applied_at timestamptz not null default now(), primary key (extension_name, version) )", ) .execute(pool) .await?; for extension in extensions { for migration in extension.migrations() { let already_applied = query( "select 1 from __crank_ext_migrations where extension_name = $1 and version = $2", ) .bind(extension.name()) .bind(i32::try_from(migration.version).map_err(|_| { RegistryError::InvalidNumericValue { field: "extension_migration.version", value: migration.version as i64, } })?) .fetch_optional(pool) .await? .is_some(); if already_applied { continue; } let mut tx = pool.begin().await?; query(migration.sql).execute(&mut *tx).await?; query( "insert into __crank_ext_migrations (extension_name, version) values ($1, $2)", ) .bind(extension.name()) .bind(i32::try_from(migration.version).map_err(|_| { RegistryError::InvalidNumericValue { field: "extension_migration.version", value: migration.version as i64, } })?) .execute(&mut *tx) .await?; tx.commit().await?; } } Ok(()) }