pg_migrator/
lib.rs

1use std::path::Path;
2
3#[cfg(feature = "postgres")]
4use postgres::Client;
5#[cfg(feature = "tokio-postgres")]
6use tokio_postgres::Client;
7
8use crate::error::MigrationError;
9
10mod error;
11mod fs;
12mod migration;
13
14const DEFAULT_MIGRATIONS_TABLE: &str = "__migrations";
15
16pub struct PostgresMigrator<P: AsRef<Path>> {
17    migrations_path: P,
18    migrations_table: String,
19    ignore_missing_migrations: bool,
20}
21
22impl<P: AsRef<Path>> PostgresMigrator<P> {
23    pub fn new(migrations_path: P) -> Self {
24        Self {
25            migrations_path,
26            migrations_table: DEFAULT_MIGRATIONS_TABLE.to_string(),
27            ignore_missing_migrations: false,
28        }
29    }
30
31    pub fn migrations_table<T: Into<String>>(mut self, migrations_table: T) -> Self {
32        self.migrations_table = migrations_table.into();
33        self
34    }
35
36    pub fn ignore_missing_migrations(mut self, ignore_missing: bool) -> Self {
37        self.ignore_missing_migrations = ignore_missing;
38        self
39    }
40
41    #[cfg(feature = "postgres")]
42    pub fn migrate(&self, pg: &mut Client) -> Result<(), MigrationError> {
43        let migrations = fs::load_migrations(self.migrations_path.as_ref())?;
44
45        migration::ensure_migrations_table_exists(pg, &self.migrations_table)?;
46
47        let applied = migration::validate_applied(
48            pg,
49            &self.migrations_table,
50            self.ignore_missing_migrations,
51            &migrations,
52        )?;
53
54        for migration in migrations {
55            if applied.contains(&migration.version) {
56                continue;
57            }
58
59            migration::apply(pg, &self.migrations_table, &migration)?;
60        }
61
62        Ok(())
63    }
64
65    #[cfg(feature = "tokio-postgres")]
66    pub async fn migrate(&self, pg: &mut Client) -> Result<(), MigrationError> {
67        let migrations = fs::load_migrations(self.migrations_path.as_ref())?;
68
69        migration::ensure_migrations_table_exists(pg, &self.migrations_table).await?;
70
71        let applied = migration::validate_applied(
72            pg,
73            &self.migrations_table,
74            self.ignore_missing_migrations,
75            &migrations,
76        )
77        .await?;
78
79        for migration in migrations {
80            if applied.contains(&migration.version) {
81                continue;
82            }
83
84            migration::apply(pg, &self.migrations_table, &migration).await?;
85        }
86
87        Ok(())
88    }
89}