use crate::error::{ServiceError, ServiceResult};
use std::path::Path;
enum MigratorInner {
Owned(sqlx::migrate::Migrator),
Embedded(&'static sqlx::migrate::Migrator),
}
pub struct Migrator {
inner: MigratorInner,
}
impl Migrator {
pub async fn new(migrations_path: &Path) -> ServiceResult<Self> {
let inner = sqlx::migrate::Migrator::new(migrations_path)
.await
.map_err(|e| ServiceError::Database(format!("failed to load migrations: {e}")))?;
Ok(Self { inner: MigratorInner::Owned(inner) })
}
pub fn embedded(migrator: &'static sqlx::migrate::Migrator) -> Self {
Self {
inner: MigratorInner::Embedded(migrator),
}
}
pub async fn run(&self, pool: &sqlx::PgPool) -> ServiceResult<()> {
let result = match &self.inner {
MigratorInner::Owned(m) => m.run(pool).await,
MigratorInner::Embedded(m) => m.run(pool).await,
};
result.map_err(|e| ServiceError::Database(format!("migration failed: {e}")))?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[tokio::test]
async fn test_new_nonexistent_path_returns_error() {
let path = PathBuf::from("/tmp/sunbeam_g2v_no_such_migrations_dir_xyz");
let result = Migrator::new(&path).await;
assert!(result.is_err(), "expected error for nonexistent path");
match result.err().unwrap() {
ServiceError::Database(msg) => {
assert!(
msg.contains("failed to load migrations"),
"unexpected message: {msg}"
);
}
other => panic!("expected ServiceError::Database, got {other:?}"),
}
}
}