diff --git a/crates/citrus-migrations/Cargo.toml b/crates/citrus-migrations/Cargo.toml index a990aa0..801dbf0 100644 --- a/crates/citrus-migrations/Cargo.toml +++ b/crates/citrus-migrations/Cargo.toml @@ -4,7 +4,11 @@ version = "0.1.0" edition.workspace = true rust-version.workspace = true license.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +description = "A library of database migration functions for citrus." +repository = "https://github.com/davidkurilla/citrus" +readme = "../../docs/README.md" [dependencies] +diesel = { version = "2.1.6", features = ["postgres"] } +diesel_migrations = "2.1.0" +toml = "0.5" \ No newline at end of file diff --git a/crates/citrus-migrations/src/lib.rs b/crates/citrus-migrations/src/lib.rs index 7d12d9a..ec1b931 100644 --- a/crates/citrus-migrations/src/lib.rs +++ b/crates/citrus-migrations/src/lib.rs @@ -1,14 +1,121 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right +use std::string::String; +use toml::Value; +use diesel::{pg::PgConnection, Connection}; +use diesel_migrations::{FileBasedMigrations, HarnessWithOutput, MigrationHarness}; + +pub struct InputData { + username: String, + password: String, + dbname: String, + host: String, + port: u16 +} + +fn get_str_or_default(toml: &Value, key: &str) -> String { + let value = toml.get(key); + + let str = + if value.is_none() { + eprintln!("Could not read toml field: \"{}\"", key); + "" + } else { + value.unwrap().as_str().unwrap_or_default() + }; + + String::from(str) +} +fn get_int_or_default(toml: &Value, key: &str) -> i64 { + let value = toml.get(key); + + if value.is_none() { + eprintln!("Could not read toml field: \"{}\"", key); + 0 + } else { + value.unwrap().as_integer().unwrap_or_default() + } +} + +impl InputData { + pub fn read(toml: &Value) -> InputData { + InputData { + username: get_str_or_default(toml, "username"), + password: get_str_or_default(toml, "password"), + dbname: get_str_or_default(toml, "dbname"), + host: get_str_or_default(toml, "host"), + port: get_int_or_default(toml, "port") as u16 + } + } + + pub fn postgres_url(&self) -> String { + format!( + "postgres://{}:{}@{}:{}/{}", + self.username, + self.password, + self.host, + self.port, + self.dbname + ) + } +} + +pub fn run_migration(toml_file_path: std::path::PathBuf, toml_table: String) { + + let toml_file = toml_file_path; + let table_name = 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 = InputData::read(table); + + let db_url = input.postgres_url(); + + println!("Attempting to connect to {}", db_url); + + let mut conn = PgConnection::establish(&db_url) + .expect("Unable to connect"); + + let migrations = FileBasedMigrations::find_migrations_directory() + .expect("Could not read migrations directory"); + + let mut harness = HarnessWithOutput::write_to_stdout(&mut conn); + + harness.run_pending_migrations(migrations).expect("Couldn't run migrations"); + + println!("Successfully ran migrations") +} + + +pub fn get_toml_table<'a>(table_name: &'a str, toml_data: &'a Value) -> &'a Value{ + if !table_name.is_empty() { + let table = toml_data.get(&table_name); + if table.is_none() { + eprintln!("Unable to find toml table: \"{}\"", &table_name); + std::process::abort() + } + + table.unwrap() + } else { + &toml_data + } } #[cfg(test)] mod tests { use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } }