From 11874bd8a4abbe109feb4577a4d1e628b5b2a85c Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Sun, 16 May 2021 16:39:21 +0300 Subject: [PATCH] feat: add migrations config to manifest - added migrations directory path - added migrations table name --- migra-cli/src/commands/apply.rs | 2 +- migra-cli/src/commands/downgrade.rs | 2 +- migra-cli/src/commands/list.rs | 2 +- migra-cli/src/commands/upgrade.rs | 2 +- migra-cli/src/config.rs | 159 +++++++++++++++++++++------- migra-cli/src/database/migration.rs | 7 +- migra-cli/tests/commands.rs | 12 +++ 7 files changed, 142 insertions(+), 44 deletions(-) diff --git a/migra-cli/src/commands/apply.rs b/migra-cli/src/commands/apply.rs index 1b22da6..0a78448 100644 --- a/migra-cli/src/commands/apply.rs +++ b/migra-cli/src/commands/apply.rs @@ -10,7 +10,7 @@ pub(crate) fn apply_sql(app: &App, cmd_opts: ApplyCommandOpt) -> StdResult<()> { let mut connection_manager = DatabaseConnectionManager::connect(&config.database)?; let conn = connection_manager.connection(); - let migration_manager = MigrationManager::new(); + let migration_manager = MigrationManager::from(&config); let file_contents = cmd_opts .file_paths diff --git a/migra-cli/src/commands/downgrade.rs b/migra-cli/src/commands/downgrade.rs index 63ee043..4b9c697 100644 --- a/migra-cli/src/commands/downgrade.rs +++ b/migra-cli/src/commands/downgrade.rs @@ -10,7 +10,7 @@ pub(crate) fn rollback_applied_migrations(app: &App, opts: DowngradeCommandOpt) let config = app.config()?; let mut connection_manager = DatabaseConnectionManager::connect(&config.database)?; let conn = connection_manager.connection(); - let migration_manager = MigrationManager::new(); + let migration_manager = MigrationManager::from(&config); let applied_migrations = migration_manager.applied_migration_names(conn)?; let migrations = config.migrations()?; diff --git a/migra-cli/src/commands/list.rs b/migra-cli/src/commands/list.rs index 0b45101..880feb6 100644 --- a/migra-cli/src/commands/list.rs +++ b/migra-cli/src/commands/list.rs @@ -16,7 +16,7 @@ pub(crate) fn print_migration_lists(app: &App) -> StdResult<()> { )?; let conn = connection_manager.connection(); - let migration_manager = MigrationManager::new(); + let migration_manager = MigrationManager::from(&config); let applied_migration_names = migration_manager.applied_migration_names(conn)?; show_applied_migrations(&applied_migration_names); diff --git a/migra-cli/src/commands/upgrade.rs b/migra-cli/src/commands/upgrade.rs index abbf088..b107c9d 100644 --- a/migra-cli/src/commands/upgrade.rs +++ b/migra-cli/src/commands/upgrade.rs @@ -10,7 +10,7 @@ pub(crate) fn upgrade_pending_migrations(app: &App, opts: UpgradeCommandOpt) -> let mut connection_manager = DatabaseConnectionManager::connect(&config.database)?; let conn = connection_manager.connection(); - let migration_manager = MigrationManager::new(); + let migration_manager = MigrationManager::from(&config); let applied_migration_names = migration_manager.applied_migration_names(conn)?; let migrations = config.migrations()?; diff --git a/migra-cli/src/config.rs b/migra-cli/src/config.rs index c5e534d..0631d88 100644 --- a/migra-cli/src/config.rs +++ b/migra-cli/src/config.rs @@ -4,11 +4,31 @@ use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; use std::{env, fs, io}; -pub(crate) const MIGRA_TOML_FILENAME: &str = "Migra.toml"; -pub(crate) const DEFAULT_DATABASE_CONNECTION_ENV: &str = "$DATABASE_URL"; +//===========================================================================// +// Default values for optional manifest variables // +//===========================================================================// -fn default_database_connection_env() -> String { - DEFAULT_DATABASE_CONNECTION_ENV.to_owned() +pub(crate) const MIGRA_TOML_FILENAME: &str = "Migra.toml"; + +//===========================================================================// +// Internal Config Utils / Macros // +//===========================================================================// + +fn search_for_directory_containing_file(path: &Path, file_name: &str) -> MigraResult { + let file_path = path.join(file_name); + if file_path.is_file() { + Ok(path.to_owned()) + } else { + path.parent() + .ok_or(Error::RootNotFound) + .and_then(|p| search_for_directory_containing_file(p, file_name)) + } +} + +fn recursive_find_project_root() -> MigraResult { + let current_dir = std::env::current_dir()?; + + search_for_directory_containing_file(¤t_dir, MIGRA_TOML_FILENAME) } #[cfg(any(not(feature = "postgres"), not(feature = "mysql")))] @@ -24,15 +44,12 @@ cargo install migra-cli --features ${database_name}"#, }; } -#[derive(Debug, Serialize, Deserialize)] -pub(crate) struct Config { - #[serde(skip)] - manifest_root: PathBuf, +//===========================================================================// +// Database config // +//===========================================================================// - root: PathBuf, - - #[serde(default)] - pub database: DatabaseConfig, +fn default_database_connection_env() -> String { + String::from("$DATABASE_URL") } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -56,7 +73,7 @@ impl Default for SupportedDatabaseClient { } } -#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct DatabaseConfig { pub client: Option, @@ -64,6 +81,15 @@ pub(crate) struct DatabaseConfig { pub connection: String, } +impl Default for DatabaseConfig { + fn default() -> Self { + DatabaseConfig { + connection: default_database_connection_env(), + client: None, + } + } +} + impl DatabaseConfig { pub fn client(&self) -> SupportedDatabaseClient { self.client.clone().unwrap_or_else(|| { @@ -103,36 +129,97 @@ impl DatabaseConfig { } } +//===========================================================================// +// Migrations config // +//===========================================================================// + +fn default_migrations_directory() -> String { + String::from("migrations") +} + +fn default_migrations_table_name() -> String { + String::from("migrations") +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct MigrationsConfig { + #[serde(rename = "directory", default = "default_migrations_directory")] + directory: String, + + #[serde(default = "default_migrations_table_name")] + table_name: String, +} + +impl Default for MigrationsConfig { + fn default() -> Self { + MigrationsConfig { + directory: default_migrations_directory(), + table_name: default_migrations_table_name(), + } + } +} + +impl MigrationsConfig { + pub fn directory(&self) -> String { + if let Some(directory_env) = self.directory.strip_prefix("$") { + env::var(directory_env).unwrap_or_else(|_| { + println!( + "WARN: Cannot read {} variable and use {} directory by default", + directory_env, + default_migrations_directory() + ); + default_migrations_directory() + }) + } else { + self.directory.clone() + } + } + + pub fn table_name(&self) -> String { + if let Some(table_name_env) = self.table_name.strip_prefix("$") { + env::var(table_name_env).unwrap_or_else(|_| { + println!( + "WARN: Cannot read {} variable and use {} table_name by default", + table_name_env, + default_migrations_table_name() + ); + default_migrations_table_name() + }) + } else { + self.table_name.clone() + } + } +} + +//===========================================================================// +// Main config // +//===========================================================================// + +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct Config { + #[serde(skip)] + manifest_root: PathBuf, + + root: PathBuf, + + #[serde(default)] + pub(crate) database: DatabaseConfig, + + #[serde(default)] + pub(crate) migrations: MigrationsConfig, +} + impl Default for Config { fn default() -> Config { Config { manifest_root: PathBuf::default(), root: PathBuf::from("database"), - database: DatabaseConfig { - connection: default_database_connection_env(), - ..Default::default() - }, + database: DatabaseConfig::default(), + migrations: MigrationsConfig::default(), } } } -fn search_for_directory_containing_file(path: &Path, file_name: &str) -> MigraResult { - let file_path = path.join(file_name); - if file_path.is_file() { - Ok(path.to_owned()) - } else { - path.parent() - .ok_or(Error::RootNotFound) - .and_then(|p| search_for_directory_containing_file(p, file_name)) - } -} - -fn recursive_find_project_root() -> MigraResult { - let current_dir = std::env::current_dir()?; - - search_for_directory_containing_file(¤t_dir, MIGRA_TOML_FILENAME) -} - impl Config { pub fn read(config_path: Option<&PathBuf>) -> MigraResult { let config_path = match config_path { @@ -160,15 +247,13 @@ impl Config { } } } -} -impl Config { pub fn directory_path(&self) -> PathBuf { self.manifest_root.join(&self.root) } pub fn migration_dir_path(&self) -> PathBuf { - self.directory_path().join("migrations") + self.directory_path().join(self.migrations.directory()) } pub fn migrations(&self) -> MigraResult> { diff --git a/migra-cli/src/database/migration.rs b/migra-cli/src/database/migration.rs index cc99763..1de6e37 100644 --- a/migra-cli/src/database/migration.rs +++ b/migra-cli/src/database/migration.rs @@ -1,4 +1,5 @@ use super::connection::AnyConnection; +use crate::Config; use crate::StdResult; use std::fs; use std::path::{Path, PathBuf}; @@ -54,10 +55,10 @@ pub struct MigrationManager { migrations_table_name: String, } -impl MigrationManager { - pub fn new() -> Self { +impl From<&Config> for MigrationManager { + fn from(config: &Config) -> Self { MigrationManager { - migrations_table_name: String::from("migrations"), + migrations_table_name: config.migrations.table_name(), } } } diff --git a/migra-cli/tests/commands.rs b/migra-cli/tests/commands.rs index e9bed51..92ddcab 100644 --- a/migra-cli/tests/commands.rs +++ b/migra-cli/tests/commands.rs @@ -44,6 +44,8 @@ mod init { fn init_manifest_with_default_config() -> TestResult { let manifest_path = "Migra.toml"; + fs::remove_file(&manifest_path).ok(); + Command::cargo_bin("migra")? .arg("init") .assert() @@ -58,6 +60,10 @@ mod init { [database] connection = "$DATABASE_URL" + +[migrations] +directory = "migrations" +table_name = "migrations" "# ); @@ -70,6 +76,8 @@ connection = "$DATABASE_URL" fn init_manifest_in_custom_path() -> TestResult { let manifest_path = path_to_file("Migra.toml"); + fs::remove_file(&manifest_path).ok(); + Command::cargo_bin("migra")? .arg("-c") .arg(&manifest_path) @@ -86,6 +94,10 @@ connection = "$DATABASE_URL" [database] connection = "$DATABASE_URL" + +[migrations] +directory = "migrations" +table_name = "migrations" "# );