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}