feat: add hsdev binary to run migrations (#4877)

Co-authored-by: James Motherwell <motherwell.james@student.greenriver.edu>
Co-authored-by: James M <122129564+JamesM25@users.noreply.github.com>
This commit is contained in:
David Kurilla
2024-07-08 10:12:25 -07:00
committed by GitHub
parent 19744cec10
commit f64b522154
5 changed files with 342 additions and 8 deletions

145
Cargo.lock generated
View File

@ -384,12 +384,55 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstream"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
[[package]]
name = "anstyle-parse"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
]
[[package]]
name = "anyhow"
version = "1.0.81"
@ -1909,8 +1952,10 @@ version = "4.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
@ -1940,6 +1985,12 @@ dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "colorchoice"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
[[package]]
name = "common_enums"
version = "0.1.0"
@ -2016,7 +2067,7 @@ dependencies = [
"rust-ini",
"serde",
"serde_json",
"toml",
"toml 0.8.12",
"yaml-rust",
]
@ -2029,7 +2080,7 @@ dependencies = [
"indexmap 2.2.6",
"serde",
"serde_json",
"toml",
"toml 0.8.12",
]
[[package]]
@ -2039,7 +2090,7 @@ dependencies = [
"api_models",
"serde",
"serde_with",
"toml",
"toml 0.8.12",
"utoipa",
]
@ -2588,9 +2639,9 @@ checksum = "b6e854126756c496b8c81dec88f9a706b15b875c5849d4097a3854476b9fdf94"
[[package]]
name = "diesel"
version = "2.1.5"
version = "2.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03fc05c17098f21b89bc7d98fe1dd3cce2c11c2ad8e145f2a44fe08ed28eb559"
checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2"
dependencies = [
"bitflags 2.5.0",
"byteorder",
@ -2604,9 +2655,9 @@ dependencies = [
[[package]]
name = "diesel_derives"
version = "2.1.3"
version = "2.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d02eecb814ae714ffe61ddc2db2dd03e6c49a42e269b5001355500d431cce0c"
checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c"
dependencies = [
"diesel_table_macro_syntax",
"proc-macro2",
@ -2614,6 +2665,17 @@ dependencies = [
"syn 2.0.57",
]
[[package]]
name = "diesel_migrations"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac"
dependencies = [
"diesel",
"migrations_internals",
"migrations_macros",
]
[[package]]
name = "diesel_models"
version = "0.1.0"
@ -3584,6 +3646,17 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "hsdev"
version = "0.1.0"
dependencies = [
"clap",
"diesel",
"diesel_migrations",
"serde",
"toml 0.5.11",
]
[[package]]
name = "http"
version = "0.2.12"
@ -4020,6 +4093,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "iso_country"
version = "0.1.4"
@ -4396,6 +4475,27 @@ dependencies = [
"autocfg",
]
[[package]]
name = "migrations_internals"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada"
dependencies = [
"serde",
"toml 0.7.8",
]
[[package]]
name = "migrations_macros"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08"
dependencies = [
"migrations_internals",
"proc-macro2",
"quote",
]
[[package]]
name = "mimalloc"
version = "0.1.39"
@ -7370,7 +7470,7 @@ dependencies = [
"thirtyfour",
"time",
"tokio 1.37.0",
"toml",
"toml 0.8.12",
]
[[package]]
@ -7798,6 +7898,27 @@ dependencies = [
"tracing",
]
[[package]]
name = "toml"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]]
name = "toml"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.19.15",
]
[[package]]
name = "toml"
version = "0.8.12"
@ -7827,6 +7948,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap 2.2.6",
"serde",
"serde_spanned",
"toml_datetime",
"winnow 0.5.40",
]
@ -8243,6 +8366,12 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "110352d4e9076c67839003c7788d8604e24dcded13e0b375af3efaa8cf468517"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "utoipa"
version = "4.2.0"

16
crates/hsdev/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "hsdev"
version = "0.1.0"
license.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "A simple diesel postgres migrator that uses TOML files"
repository = "https://github.com/juspay/hyperswitch.git"
readme = "README.md"
[dependencies]
diesel = { version = "2.1.6", features = ["postgres"] }
diesel_migrations = "2.1.0"
toml = "0.5"
clap = { version = "4.1.8", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }

24
crates/hsdev/README.md Normal file
View File

@ -0,0 +1,24 @@
# HSDEV
`hsdev` is a simple diesel Postgres migration tool. It is designed to simply running a Postgres database migration with diesel.
## Installing hsdev
`hsdev` can be installed using `cargo`
```shell
cargo install --force --path crates/hsdev
```
## Using hsdev
Using `hsdev` is simple. All you need to do is run the following command.
```shell
hsdev --toml-file [path/to/TOML/file]
```
provide `hsdev` with a TOML file containing the following keys:
```toml
username = "your_username"
password = "your_password"
dbname = "your_db_name"
```
Simply run the command and let `hsdev` handle the rest.

View File

@ -0,0 +1,26 @@
use std::string::String;
use serde::Deserialize;
use toml::Value;
#[derive(Deserialize)]
pub struct InputData {
username: String,
password: String,
dbname: String,
host: String,
port: u16,
}
impl InputData {
pub fn read(db_table: &Value) -> Result<Self, toml::de::Error> {
db_table.clone().try_into()
}
pub fn postgres_url(&self) -> String {
format!(
"postgres://{}:{}@{}:{}/{}",
self.username, self.password, self.host, self.port, self.dbname
)
}
}

139
crates/hsdev/src/main.rs Normal file
View File

@ -0,0 +1,139 @@
use clap::{Parser, ValueHint};
use diesel::{pg::PgConnection, Connection};
use diesel_migrations::{FileBasedMigrations, HarnessWithOutput, MigrationHarness};
use toml::Value;
mod input_file;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(short, long, value_hint = ValueHint::FilePath)]
toml_file: std::path::PathBuf,
#[arg(long, default_value_t = String::from(""))]
toml_table: String,
}
fn main() {
let args = Args::parse();
let toml_file = &args.toml_file;
let table_name = &args.toml_table;
let toml_contents = match std::fs::read_to_string(toml_file) {
Ok(contents) => contents,
Err(e) => {
eprintln!("Error reading TOML file: {}", e);
return;
}
};
let toml_data: Value = match toml_contents.parse() {
Ok(data) => data,
Err(e) => {
eprintln!("Error parsing TOML file: {}", e);
return;
}
};
let table = get_toml_table(table_name, &toml_data);
let input = match input_file::InputData::read(table) {
Ok(data) => data,
Err(e) => {
eprintln!("Error loading TOML file: {}", e);
return;
}
};
let db_url = input.postgres_url();
println!("Attempting to connect to {}", db_url);
let mut conn = match PgConnection::establish(&db_url) {
Ok(value) => value,
Err(_) => {
eprintln!("Unable to establish database connection");
return;
}
};
let migrations = match FileBasedMigrations::find_migrations_directory() {
Ok(value) => value,
Err(_) => {
eprintln!("Could not find migrations directory");
return;
}
};
let mut harness = HarnessWithOutput::write_to_stdout(&mut conn);
match harness.run_pending_migrations(migrations) {
Ok(_) => println!("Successfully ran migrations"),
Err(_) => eprintln!("Couldn't run migrations"),
};
}
pub fn get_toml_table<'a>(table_name: &'a str, toml_data: &'a Value) -> &'a Value {
if !table_name.is_empty() {
match toml_data.get(table_name) {
Some(value) => value,
None => {
eprintln!("Unable to find toml table: \"{}\"", &table_name);
std::process::abort()
}
}
} else {
toml_data
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use std::str::FromStr;
use toml::Value;
use crate::{get_toml_table, input_file::InputData};
#[test]
fn test_input_file() {
let toml_str = r#"username = "db_user"
password = "db_pass"
dbname = "db_name"
host = "localhost"
port = 5432"#;
let toml_value = Value::from_str(toml_str);
assert!(toml_value.is_ok());
let toml_value = toml_value.unwrap();
let toml_table = InputData::read(&toml_value);
assert!(toml_table.is_ok());
let toml_table = toml_table.unwrap();
let db_url = toml_table.postgres_url();
assert_eq!("postgres://db_user:db_pass@localhost:5432/db_name", db_url);
}
#[test]
fn test_given_toml() {
let toml_str_table = r#"[database]
username = "db_user"
password = "db_pass"
dbname = "db_name"
host = "localhost"
port = 5432"#;
let table_name = "database";
let toml_value = Value::from_str(toml_str_table).unwrap();
let table = get_toml_table(table_name, &toml_value);
assert!(table.is_table());
let table_name = "";
let table = get_toml_table(table_name, &toml_value);
assert!(table.is_table());
}
}