feat: add migrations config to manifest

- added migrations directory path
- added migrations table name
This commit is contained in:
Dmitriy Pleshevskiy 2021-05-16 16:39:21 +03:00
parent 83a4155d76
commit 11874bd8a4
7 changed files with 142 additions and 44 deletions

View File

@ -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

View File

@ -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()?;

View File

@ -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);

View File

@ -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()?;

View File

@ -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<PathBuf> {
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<PathBuf> {
let current_dir = std::env::current_dir()?;
search_for_directory_containing_file(&current_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<SupportedDatabaseClient>,
@ -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<PathBuf> {
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<PathBuf> {
let current_dir = std::env::current_dir()?;
search_for_directory_containing_file(&current_dir, MIGRA_TOML_FILENAME)
}
impl Config {
pub fn read(config_path: Option<&PathBuf>) -> MigraResult<Config> {
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<Vec<Migration>> {

View File

@ -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(),
}
}
}

View File

@ -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"
"#
);