1use std::marker::PhantomData;
2
3use crate::{Result, SqlxDBNum};
4use futures::future::BoxFuture;
5use sqlx::{
6 SqlStr,
7 migrate::{Migration as SqlxMigration, MigrationSource, MigrationType},
8};
9
10#[derive(Debug)]
11pub struct RepoMigrationSource<D> {
12 migrations: Vec<Migration>,
13 marker: PhantomData<D>,
14}
15
16#[derive(Debug, Clone, Copy)]
17pub struct Migration {
18 pub name: &'static str,
19 pub queries: &'static [&'static str],
20}
21
22impl<'a, D: SqlxDBNum> MigrationSource<'a> for RepoMigrationSource<D> {
23 fn resolve(self) -> BoxFuture<'a, Result<Vec<SqlxMigration>, sqlx::error::BoxDynError>> {
24 Box::pin(async move {
25 let migrations = self.migrations
26 .iter()
27 .enumerate()
28 .map(|(pos, migration)| {
29 let query_pos = D::pos();
30 let query = match migration.queries.get(query_pos) {
31 Some(&query) => query,
32 None => Err("failed to generate migration, tried to get query at index {query_pos}, which doesn't exist")?
33 };
34 #[allow(clippy::cast_possible_wrap)]
35 Ok(SqlxMigration::new(pos as _, migration.name.into(), MigrationType::Simple, SqlStr::from_static(query), false))
36 })
37 .collect::<Result<_>>()?;
38 Ok(migrations)
39 })
40 }
41}
42
43impl<D: SqlxDBNum> Default for RepoMigrationSource<D> {
44 fn default() -> Self {
45 Self::new()
46 }
47}
48
49impl<D: SqlxDBNum> RepoMigrationSource<D> {
50 pub fn new() -> Self {
51 Self {
52 migrations: vec![],
53 marker: PhantomData,
54 }
55 }
56
57 pub fn add_migration(&mut self, migration: Migration) {
58 self.migrations.push(migration);
59 }
60}
61
62pub async fn init_migrator<D: SqlxDBNum>(
63 migrations: &[Migration],
64) -> Result<sqlx::migrate::Migrator, sqlx::migrate::MigrateError> {
65 let mut source = RepoMigrationSource::<D>::new();
66 for migration in migrations {
67 source.add_migration(*migration);
68 }
69 sqlx::migrate::Migrator::new(source).await
70}