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

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());
}
}