From 032e1b7287d778244a58afd5c647e7d35852aa5a Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Mon, 8 Feb 2021 07:01:31 +0300 Subject: [PATCH] feat: read database url from env variable Now we can use migra cli without config initialization --- migra-cli/src/config.rs | 129 +++++++++++++++++++++++++--------------- migra-cli/src/main.rs | 16 +++-- 2 files changed, 90 insertions(+), 55 deletions(-) diff --git a/migra-cli/src/config.rs b/migra-cli/src/config.rs index 0b8ee30..cbf58b0 100644 --- a/migra-cli/src/config.rs +++ b/migra-cli/src/config.rs @@ -3,21 +3,25 @@ use crate::path::PathBuilder; use postgres::Client; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; -use std::{fs, io}; +use std::{env, fs, io}; const MIGRA_TOML_FILENAME: &str = "Migra.toml"; +const DEFAULT_DATABASE_CONNECTION_ENV: &str = "$DATABASE_URL"; #[derive(Debug, Serialize, Deserialize)] pub(crate) struct Config { #[serde(skip)] - pub root: PathBuf, - pub directory: PathBuf, - pub database: DatabaseConfig, + root: PathBuf, + + directory: PathBuf, + + #[serde(default)] + database: DatabaseConfig, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, Serialize, Deserialize)] pub(crate) struct DatabaseConfig { - pub connection: String, + pub connection: Option, } impl Default for Config { @@ -26,7 +30,7 @@ impl Default for Config { root: PathBuf::new(), directory: PathBuf::from("database"), database: DatabaseConfig { - connection: String::new(), + connection: Some(String::from(DEFAULT_DATABASE_CONNECTION_ENV)), }, } } @@ -57,21 +61,26 @@ impl Config { let config_path = match config_path { Some(mut config_path) if config_path.is_dir() => { config_path.push(MIGRA_TOML_FILENAME); - config_path + Some(config_path) } - Some(config_path) => config_path, - None => recursive_find_config_file()?, + Some(config_path) => Some(config_path), + None => recursive_find_config_file().ok(), }; - let content = fs::read_to_string(&config_path)?; + match config_path { + None => Ok(Config::default()), + Some(config_path) => { + let content = fs::read_to_string(&config_path)?; - let mut config: Config = toml::from_str(&content).expect("Cannot parse Migra.toml"); - config.root = config_path - .parent() - .unwrap_or_else(|| Path::new("")) - .to_path_buf(); + let mut config: Config = toml::from_str(&content).expect("Cannot parse Migra.toml"); + config.root = config_path + .parent() + .unwrap_or_else(|| Path::new("")) + .to_path_buf(); - Ok(config) + Ok(config) + } + } } pub fn initialize() -> Result<(), Box> { @@ -90,6 +99,56 @@ impl Config { } } +impl Config { + pub fn directory_path(&self) -> PathBuf { + PathBuilder::from(&self.root) + .append(&self.directory) + .build() + } + + pub fn database_connection(&self) -> String { + let connection = self + .database + .connection + .clone() + .unwrap_or_else(|| String::from(DEFAULT_DATABASE_CONNECTION_ENV)); + if let Some(connection_env) = connection.strip_prefix("$") { + env::var(connection_env).unwrap_or_else(|_| { + panic!( + r#"You need to provide "{}" environment variable"#, + connection_env + ) + }) + } else { + connection + } + } + + pub fn migration_dir_path(&self) -> PathBuf { + PathBuilder::from(&self.directory_path()) + .append("migrations") + .build() + } + + pub fn migrations(&self) -> io::Result> { + let mut entries = self + .migration_dir_path() + .read_dir()? + .map(|res| res.map(|e| e.path())) + .collect::, io::Error>>()?; + + entries.sort(); + + let migrations = entries + .iter() + .filter_map(Migration::new) + .collect::>(); + + Ok(migrations) + } +} + +#[derive(Debug)] pub struct Migration { upgrade_sql: PathBuf, downgrade_sql: PathBuf, @@ -134,7 +193,10 @@ impl Migration { Ok(()) } - pub fn downgrade(&self, client: &mut Client) -> Result<(), Box> { + pub fn downgrade( + &self, + client: &mut Client, + ) -> Result<(), Box> { let content = fs::read_to_string(&self.downgrade_sql)?; database::apply_sql(client, &content)?; @@ -144,34 +206,3 @@ impl Migration { Ok(()) } } - -impl Config { - pub fn directory_path(&self) -> PathBuf { - PathBuilder::from(&self.root) - .append(&self.directory) - .build() - } - - pub fn migration_dir_path(&self) -> PathBuf { - PathBuilder::from(&self.directory_path()) - .append("migrations") - .build() - } - - pub fn migrations(&self) -> io::Result> { - let mut entries = self - .migration_dir_path() - .read_dir()? - .map(|res| res.map(|e| e.path())) - .collect::, io::Error>>()?; - - entries.sort(); - - let migrations = entries - .iter() - .filter_map(Migration::new) - .collect::>(); - - Ok(migrations) - } -} diff --git a/migra-cli/src/main.rs b/migra-cli/src/main.rs index 4bb97ea..d734dbc 100644 --- a/migra-cli/src/main.rs +++ b/migra-cli/src/main.rs @@ -21,7 +21,7 @@ fn main() -> Result<(), Box> { Command::Apply(ApplyCommandOpt { file_name }) => { let config = Config::read(opt.config)?; - let mut client = database::connect(&config.database.connection)?; + let mut client = database::connect(&config.database_connection())?; let file_path = PathBuilder::from(config.directory_path()) .append(file_name) @@ -80,7 +80,7 @@ fn main() -> Result<(), Box> { Command::List => { let config = Config::read(opt.config)?; - let mut client = database::connect(&config.database.connection)?; + let mut client = database::connect(&config.database_connection())?; let applied_migrations = database::applied_migrations(&mut client)?; println!("Applied migrations:"); @@ -95,7 +95,8 @@ fn main() -> Result<(), Box> { println!(); - let pending_migrations = config.migrations()? + let pending_migrations = config + .migrations()? .into_iter() .filter(|m| !applied_migrations.contains(m.name())) .collect::>(); @@ -111,7 +112,7 @@ fn main() -> Result<(), Box> { Command::Upgrade => { let config = Config::read(opt.config)?; - let mut client = database::connect(&config.database.connection)?; + let mut client = database::connect(&config.database_connection())?; let applied_migrations = database::applied_migrations(&mut client)?; @@ -134,13 +135,16 @@ fn main() -> Result<(), Box> { Command::Downgrade => { let config = Config::read(opt.config)?; - let mut client = database::connect(&config.database.connection)?; + let mut client = database::connect(&config.database_connection())?; let applied_migrations = database::applied_migrations(&mut client)?; let migrations = config.migrations()?; if let Some(first_applied_migration) = applied_migrations.first() { - if let Some(migration) = migrations.iter().find(|m| m.name() == first_applied_migration) { + if let Some(migration) = migrations + .iter() + .find(|m| m.name() == first_applied_migration) + { println!("downgrade {}...", migration.name()); migration.downgrade(&mut client)?; }