diesel_migrations/combined_migrations.rs
1use std::sync::Arc;
2
3use diesel::backend::Backend;
4use diesel::migration::{Migration, MigrationSource};
5
6/// A diesel migration source that combines several other sources
7///
8/// This source will act like all migrations came from a single source.
9/// It orders all the migrations by version
10///
11/// # Example
12/// ```
13/// # include!("../../diesel/src/doctest_setup.rs");
14/// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
15/// use diesel::prelude::*;
16/// use diesel_migrations::EmbeddedMigrations;
17/// use diesel_migrations::CombinedMigrationSource;
18/// use crate::diesel_migrations::MigrationHarness;
19/// use migrations_macros::embed_migrations;
20///
21/// pub const PG_MIGRATIONS: EmbeddedMigrations = embed_migrations!("../migrations/postgres");
22/// pub const SQLITE_MIGRATIONS: EmbeddedMigrations = embed_migrations!("../migrations/sqlite");
23///
24/// # #[cfg(feature = "postgres")]
25/// # let connection_url = database_url_from_env("PG_DATABASE_URL");
26/// # // An in-memory database avoids lock races between concurrently running doctests
27/// # // and keeps this example's partially applied migrations out of the shared test database.
28/// # #[cfg(feature = "sqlite")]
29/// # let connection_url = String::from(":memory:");
30/// # #[cfg(feature = "mysql")]
31/// # let connection_url = database_url_from_env("MYSQL_DATABASE_URL");
32/// # #[cfg(feature = "mariadb")]
33/// # let connection_url = database_url_from_env("MARIADB_DATABASE_URL");
34/// # #[cfg(feature = "postgres")]
35/// # type SqliteConnection = PgConnection;
36/// # #[cfg(feature = "mysql")]
37/// # type SqliteConnection = MysqlConnection;
38/// # #[cfg(feature = "mariadb")]
39/// # type SqliteConnection = MariadbConnection;
40/// #
41/// // Create a new empty combined source
42/// let mut combined_sources = CombinedMigrationSource::default();
43///
44/// // It's not particular meaningful to combine PostgreSQL and SQLite like this,
45/// // but that reasonable demonstrates the API
46/// combined_sources.add_source(PG_MIGRATIONS);
47/// combined_sources.add_source(SQLITE_MIGRATIONS);
48///
49/// // run the migrations
50/// let mut connection = SqliteConnection::establish(&connection_url)?;
51/// let res = connection.run_pending_migrations(combined_sources);
52/// # assert!(res.is_err(), "This is supposed to fail as you cannot run postgres migrations using sqlite");
53/// # Ok(())
54/// # }
55/// ```
56#[derive(Default, Clone)]
57pub struct CombinedMigrationSource<DB> {
58 sources: Vec<Arc<dyn MigrationSource<DB> + Send + Sync>>,
59}
60
61impl<DB> CombinedMigrationSource<DB>
62where
63 DB: Backend,
64{
65 /// Register another source with the given migration source
66 pub fn add_source(&mut self, source: impl MigrationSource<DB> + Send + Sync + 'static) {
67 self.sources.push(Arc::new(source))
68 }
69}
70
71impl<DB> MigrationSource<DB> for CombinedMigrationSource<DB>
72where
73 DB: Backend,
74{
75 fn migrations(&self) -> diesel::migration::Result<Vec<Box<dyn Migration<DB>>>> {
76 let mut migrations = Vec::new();
77 for source in &self.sources {
78 migrations.extend(source.migrations()?);
79 }
80 migrations.sort_by(|m1, m2| m1.name().version().cmp(&m2.name().version()));
81 Ok(migrations)
82 }
83}