From 40c0a43dab7dc3d8def2e5b1420b69456b75e076 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Sat, 13 Feb 2021 00:39:39 +0300 Subject: [PATCH] refac: move commands to separate folder --- migra-cli/src/commands/apply.rs | 28 +++++ migra-cli/src/commands/downgrade.rs | 23 ++++ migra-cli/src/commands/init.rs | 34 ++++++ migra-cli/src/commands/list.rs | 51 +++++++++ migra-cli/src/commands/make.rs | 45 ++++++++ migra-cli/src/commands/mod.rs | 13 +++ migra-cli/src/commands/upgrade.rs | 26 +++++ migra-cli/src/config.rs | 46 ++------ migra-cli/src/error.rs | 1 + migra-cli/src/main.rs | 156 +++------------------------- 10 files changed, 242 insertions(+), 181 deletions(-) create mode 100644 migra-cli/src/commands/apply.rs create mode 100644 migra-cli/src/commands/downgrade.rs create mode 100644 migra-cli/src/commands/init.rs create mode 100644 migra-cli/src/commands/list.rs create mode 100644 migra-cli/src/commands/make.rs create mode 100644 migra-cli/src/commands/mod.rs create mode 100644 migra-cli/src/commands/upgrade.rs diff --git a/migra-cli/src/commands/apply.rs b/migra-cli/src/commands/apply.rs new file mode 100644 index 0000000..75c75fd --- /dev/null +++ b/migra-cli/src/commands/apply.rs @@ -0,0 +1,28 @@ +use crate::config::Config; +use crate::database; +use crate::opts::ApplyCommandOpt; +use crate::path::PathBuilder; +use crate::StdResult; + +pub(crate) fn apply_sql(config: Config, opts: ApplyCommandOpt) -> StdResult<()> { + let database_connection_string = &config.database_connection_string()?; + let mut client = database::connect(database_connection_string)?; + + let file_path = PathBuilder::from(config.directory_path()) + .append(opts.file_name) + .default_extension("sql") + .build(); + + let content = std::fs::read_to_string(file_path)?; + + match database::apply_sql(&mut client, &content) { + Ok(_) => { + println!("File was applied successfully"); + } + Err(err) => { + println!("{}", err); + } + } + + Ok(()) +} diff --git a/migra-cli/src/commands/downgrade.rs b/migra-cli/src/commands/downgrade.rs new file mode 100644 index 0000000..80f41e9 --- /dev/null +++ b/migra-cli/src/commands/downgrade.rs @@ -0,0 +1,23 @@ +use crate::config::Config; +use crate::database; +use crate::StdResult; + +pub(crate) fn downgrade_applied_migrations(config: Config) -> StdResult<()> { + let database_connection_string = &config.database_connection_string()?; + let mut client = database::connect(database_connection_string)?; + + 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) + { + println!("downgrade {}...", migration.name()); + migration.downgrade(&mut client)?; + } + } + + Ok(()) +} diff --git a/migra-cli/src/commands/init.rs b/migra-cli/src/commands/init.rs new file mode 100644 index 0000000..fbfe6ba --- /dev/null +++ b/migra-cli/src/commands/init.rs @@ -0,0 +1,34 @@ +use crate::config::Config; +use crate::config::MIGRA_TOML_FILENAME; +use crate::StdResult; +use std::path::PathBuf; + +pub(crate) fn initialize_migra_manifest(config_path: Option) -> StdResult<()> { + let config_path = config_path + .map(|mut config_path| { + let ext = config_path.extension(); + if config_path.is_dir() || ext.is_none() { + config_path.push(MIGRA_TOML_FILENAME); + } + + config_path + }) + .unwrap_or_else(|| PathBuf::from(MIGRA_TOML_FILENAME)); + + if config_path.exists() { + println!("{} already exists", config_path.to_str().unwrap()); + return Ok(()); + } + + if let Some(dirs) = config_path.parent() { + std::fs::create_dir_all(dirs)?; + } + + let config = Config::default(); + let content = toml::to_string(&config)?; + std::fs::write(&config_path, content)?; + + println!("Created {}", config_path.to_str().unwrap()); + + Ok(()) +} diff --git a/migra-cli/src/commands/list.rs b/migra-cli/src/commands/list.rs new file mode 100644 index 0000000..2507d48 --- /dev/null +++ b/migra-cli/src/commands/list.rs @@ -0,0 +1,51 @@ +use crate::config::Config; +use crate::database; +use crate::error::{ErrorKind, StdResult}; + +const EM_DASH: char = '—'; + +pub(crate) fn print_migration_lists(config: Config) -> StdResult<()> { + let applied_migrations = match config.database_connection_string() { + Ok(ref database_connection_string) => { + let mut client = database::connect(database_connection_string)?; + let applied_migrations = database::applied_migrations(&mut client)?; + + println!("Applied migrations:"); + if applied_migrations.is_empty() { + println!("{}", EM_DASH); + } else { + applied_migrations + .iter() + .rev() + .for_each(|name| println!("{}", name)); + } + + applied_migrations + } + Err(e) if *e.kind() == ErrorKind::MissedEnvVar(String::new()) => { + println!("{}", e.kind()); + println!("No connection to database"); + + Vec::new() + } + Err(e) => panic!(e), + }; + + println!(); + + let pending_migrations = config + .migrations()? + .into_iter() + .filter(|m| !applied_migrations.contains(m.name())) + .collect::>(); + println!("Pending migrations:"); + if pending_migrations.is_empty() { + println!("{}", EM_DASH); + } else { + pending_migrations.iter().for_each(|m| { + println!("{}", m.name()); + }); + } + + Ok(()) +} diff --git a/migra-cli/src/commands/make.rs b/migra-cli/src/commands/make.rs new file mode 100644 index 0000000..d24f97d --- /dev/null +++ b/migra-cli/src/commands/make.rs @@ -0,0 +1,45 @@ +use crate::opts::MakeCommandOpt; +use crate::path::PathBuilder; +use crate::Config; +use crate::StdResult; +use chrono::Local; + +pub(crate) fn make_migration(config: Config, opts: MakeCommandOpt) -> StdResult<()> { + let now = Local::now().format("%y%m%d%H%M%S"); + + let migration_name: String = opts + .migration_name + .to_lowercase() + .chars() + .map(|c| match c { + '0'..='9' | 'a'..='z' => c, + _ => '_', + }) + .collect(); + + let migration_dir_path = PathBuilder::from(config.migration_dir_path()) + .append(format!("{}_{}", now, migration_name)) + .build(); + if !migration_dir_path.exists() { + std::fs::create_dir_all(&migration_dir_path)?; + } + + let upgrade_migration_path = PathBuilder::from(&migration_dir_path) + .append("up.sql") + .build(); + if !upgrade_migration_path.exists() { + std::fs::write(upgrade_migration_path, "-- Your SQL goes here\n\n")?; + } + + let downgrade_migration_path = PathBuilder::from(&migration_dir_path) + .append("down.sql") + .build(); + if !downgrade_migration_path.exists() { + std::fs::write( + downgrade_migration_path, + "-- This file should undo anything in `up.sql`\n\n", + )?; + } + + Ok(()) +} diff --git a/migra-cli/src/commands/mod.rs b/migra-cli/src/commands/mod.rs new file mode 100644 index 0000000..1e5a9e1 --- /dev/null +++ b/migra-cli/src/commands/mod.rs @@ -0,0 +1,13 @@ +mod apply; +mod downgrade; +mod init; +mod list; +mod make; +mod upgrade; + +pub(crate) use apply::*; +pub(crate) use downgrade::*; +pub(crate) use init::*; +pub(crate) use list::*; +pub(crate) use make::*; +pub(crate) use upgrade::*; diff --git a/migra-cli/src/commands/upgrade.rs b/migra-cli/src/commands/upgrade.rs new file mode 100644 index 0000000..efdeaa9 --- /dev/null +++ b/migra-cli/src/commands/upgrade.rs @@ -0,0 +1,26 @@ +use crate::database; +use crate::Config; +use crate::StdResult; + +pub(crate) fn upgrade_pending_migrations(config: Config) -> StdResult<()> { + let database_connection_string = &config.database_connection_string()?; + let mut client = database::connect(database_connection_string)?; + + let applied_migrations = database::applied_migrations(&mut client)?; + + let migrations = config.migrations()?; + + if migrations.is_empty() || migrations.last().map(|m| m.name()) == applied_migrations.first() { + println!("Up to date"); + } else { + for m in migrations + .iter() + .filter(|m| !applied_migrations.contains(m.name())) + { + println!("upgrade {}...", m.name()); + m.upgrade(&mut client)?; + } + } + + Ok(()) +} diff --git a/migra-cli/src/config.rs b/migra-cli/src/config.rs index 42dfd13..8d57b59 100644 --- a/migra-cli/src/config.rs +++ b/migra-cli/src/config.rs @@ -5,15 +5,15 @@ use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; use std::{env, fs, io}; -const MIGRA_TOML_FILENAME: &str = "Migra.toml"; -const DEFAULT_DATABASE_CONNECTION_ENV: &str = "$DATABASE_URL"; +pub(crate) const MIGRA_TOML_FILENAME: &str = "Migra.toml"; +pub(crate) const DEFAULT_DATABASE_CONNECTION_ENV: &str = "$DATABASE_URL"; #[derive(Debug, Serialize, Deserialize)] pub(crate) struct Config { #[serde(skip)] - root: PathBuf, + manifest_root: PathBuf, - directory: PathBuf, + root: PathBuf, #[serde(default)] database: DatabaseConfig, @@ -27,8 +27,8 @@ pub(crate) struct DatabaseConfig { impl Default for Config { fn default() -> Config { Config { - root: PathBuf::new(), - directory: PathBuf::from("database"), + manifest_root: PathBuf::new(), + root: PathBuf::from("database"), database: DatabaseConfig { connection: Some(String::from(DEFAULT_DATABASE_CONNECTION_ENV)), }, @@ -73,7 +73,7 @@ impl Config { 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 + config.manifest_root = config_path .parent() .unwrap_or_else(|| Path::new("")) .to_path_buf(); @@ -82,40 +82,12 @@ impl Config { } } } - - pub fn initialize(config_path: Option) -> Result<(), Box> { - let config_path = config_path - .map(|mut config_path| { - let ext = config_path.extension(); - if config_path.is_dir() || ext.is_none() { - config_path.push(MIGRA_TOML_FILENAME); - } - - config_path - }) - .unwrap_or_else(|| PathBuf::from(MIGRA_TOML_FILENAME)); - - if config_path.exists() { - println!("{} already exists", config_path.to_str().unwrap()); - return Ok(()); - } else if let Some(dirs) = config_path.parent() { - fs::create_dir_all(dirs)?; - } - - let config = Config::default(); - let content = toml::to_string(&config)?; - fs::write(&config_path, content)?; - - println!("Created {}", config_path.to_str().unwrap()); - - Ok(()) - } } impl Config { pub fn directory_path(&self) -> PathBuf { - PathBuilder::from(&self.root) - .append(&self.directory) + PathBuilder::from(&self.manifest_root) + .append(&self.root) .build() } diff --git a/migra-cli/src/error.rs b/migra-cli/src/error.rs index c5c7811..847ec2f 100644 --- a/migra-cli/src/error.rs +++ b/migra-cli/src/error.rs @@ -3,6 +3,7 @@ use std::fmt; use std::mem; use std::result; +pub type StdResult = result::Result>; pub type Result = result::Result; #[derive(Debug)] diff --git a/migra-cli/src/main.rs b/migra-cli/src/main.rs index 33189c7..8a27d86 100644 --- a/migra-cli/src/main.rs +++ b/migra-cli/src/main.rs @@ -1,5 +1,6 @@ #![deny(clippy::all)] +mod commands; mod config; mod database; mod error; @@ -7,169 +8,36 @@ mod migration; mod opts; mod path; -use chrono::Local; +use crate::error::StdResult; use config::Config; -use error::ErrorKind; -use opts::{AppOpt, ApplyCommandOpt, Command, MakeCommandOpt, StructOpt}; -use path::PathBuilder; -use std::fs; +use opts::{AppOpt, Command, StructOpt}; -const EM_DASH: char = '—'; - -fn main() -> Result<(), Box> { +fn main() -> StdResult<()> { let opt = AppOpt::from_args(); match opt.command { Command::Init => { - Config::initialize(opt.config)?; + commands::initialize_migra_manifest(opt.config)?; } - Command::Apply(ApplyCommandOpt { file_name }) => { + Command::Apply(opts) => { let config = Config::read(opt.config)?; - - let database_connection_string = &config.database_connection_string()?; - let mut client = database::connect(database_connection_string)?; - - let file_path = PathBuilder::from(config.directory_path()) - .append(file_name) - .default_extension("sql") - .build(); - - let content = fs::read_to_string(file_path)?; - - match database::apply_sql(&mut client, &content) { - Ok(_) => { - println!("File was applied successfully") - } - Err(err) => { - println!("{}", err) - } - } + commands::apply_sql(config, opts)?; } - Command::Make(MakeCommandOpt { migration_name }) => { + Command::Make(opts) => { let config = Config::read(opt.config)?; - - let now = Local::now().format("%y%m%d%H%M%S"); - - let migration_name: String = migration_name - .to_lowercase() - .chars() - .map(|c| match c { - '0'..='9' | 'a'..='z' => c, - _ => '_', - }) - .collect(); - - let migration_dir_path = PathBuilder::from(config.migration_dir_path()) - .append(format!("{}_{}", now, migration_name)) - .build(); - if !migration_dir_path.exists() { - fs::create_dir_all(&migration_dir_path)?; - } - - let upgrade_migration_path = PathBuilder::from(&migration_dir_path) - .append("up.sql") - .build(); - if !upgrade_migration_path.exists() { - fs::write(upgrade_migration_path, "-- Your SQL goes here\n\n")?; - } - - let downgrade_migration_path = PathBuilder::from(&migration_dir_path) - .append("down.sql") - .build(); - if !downgrade_migration_path.exists() { - fs::write( - downgrade_migration_path, - "-- This file should undo anything in `up.sql`\n\n", - )?; - } + commands::make_migration(config, opts)?; } Command::List => { let config = Config::read(opt.config)?; - - let applied_migrations = match config.database_connection_string() { - Ok(ref database_connection_string) => { - let mut client = database::connect(database_connection_string)?; - let applied_migrations = database::applied_migrations(&mut client)?; - - println!("Applied migrations:"); - if applied_migrations.is_empty() { - println!("{}", EM_DASH); - } else { - applied_migrations - .iter() - .rev() - .for_each(|name| println!("{}", name)); - } - - applied_migrations - } - Err(e) if *e.kind() == ErrorKind::MissedEnvVar(String::new()) => { - println!("{}", e.kind()); - println!("No connection to database"); - - Vec::new() - } - Err(e) => panic!(e), - }; - - println!(); - - let pending_migrations = config - .migrations()? - .into_iter() - .filter(|m| !applied_migrations.contains(m.name())) - .collect::>(); - println!("Pending migrations:"); - if pending_migrations.is_empty() { - println!("{}", EM_DASH); - } else { - pending_migrations.iter().for_each(|m| { - println!("{}", m.name()); - }); - } + commands::print_migration_lists(config)?; } Command::Upgrade => { let config = Config::read(opt.config)?; - - let database_connection_string = &config.database_connection_string()?; - let mut client = database::connect(database_connection_string)?; - - let applied_migrations = database::applied_migrations(&mut client)?; - - let migrations = config.migrations()?; - - if migrations.is_empty() - || migrations.last().map(|m| m.name()) == applied_migrations.first() - { - println!("Up to date"); - } else { - for m in migrations - .iter() - .filter(|m| !applied_migrations.contains(m.name())) - { - println!("upgrade {}...", m.name()); - m.upgrade(&mut client)?; - } - } + commands::upgrade_pending_migrations(config)?; } Command::Downgrade => { let config = Config::read(opt.config)?; - - let database_connection_string = &config.database_connection_string()?; - let mut client = database::connect(database_connection_string)?; - - 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) - { - println!("downgrade {}...", migration.name()); - migration.downgrade(&mut client)?; - } - } + commands::downgrade_applied_migrations(config)?; } }