use std::path::{Path, PathBuf};
use sqlx::{
Connection as _,
migrate::{MigrateError as SqlxMigrateError, Migrator},
postgres::PgConnection,
};
use thiserror::Error;
use crate::postgres::Config;
#[derive(Debug, Error)]
pub enum MigrateError {
#[error("Postgres migration directory resolution failed at {path}")]
Resolve {
path: PathBuf,
#[source]
source: SqlxMigrateError,
},
#[error("Postgres connection failed")]
Connect(#[source] sqlx::Error),
#[error("Postgres migration failed")]
Migrate(#[source] SqlxMigrateError),
#[error("Postgres connection close failed")]
Close(#[source] sqlx::Error),
#[error("Postgres migration failed; connection close also failed: {close}")]
MigrateAndClose {
#[source]
migrate: SqlxMigrateError,
close: sqlx::Error,
},
}
pub async fn run(directory: &Path, config: &Config) -> Result<(), MigrateError> {
let migrator = Migrator::new(directory)
.await
.map_err(|source| MigrateError::Resolve {
path: directory.to_path_buf(),
source,
})?;
run_with_migrator(&migrator, config).await
}
pub async fn run_with_migrator(migrator: &Migrator, config: &Config) -> Result<(), MigrateError> {
let options = config.into();
let mut connection = PgConnection::connect_with(&options)
.await
.map_err(MigrateError::Connect)?;
let migration_result = migrator.run(&mut connection).await;
let close_result = connection.close().await;
match (migration_result, close_result) {
(Ok(()), Ok(())) => Ok(()),
(Ok(()), Err(error)) => Err(MigrateError::Close(error)),
(Err(error), Ok(())) => Err(MigrateError::Migrate(error)),
(Err(migrate), Err(close)) => Err(MigrateError::MigrateAndClose { migrate, close }),
}
}