citrus_migrations/
lib.rs

1use std::string::String;
2use toml::Value;
3use diesel::{pg::PgConnection, Connection};
4use diesel_migrations::{FileBasedMigrations, HarnessWithOutput, MigrationHarness};
5
6pub struct InputData {
7    username: String,
8    password: String,
9    dbname: String,
10    host: String,
11    port: u16
12}
13
14fn get_str_or_default(toml: &Value, key: &str) -> String {
15    let value = toml.get(key);
16
17    let str =
18    if value.is_none() {
19        eprintln!("Could not read toml field: \"{}\"", key);
20        ""
21    } else {
22        value.unwrap().as_str().unwrap_or_default()
23    };
24
25    String::from(str)
26}
27fn get_int_or_default(toml: &Value, key: &str) -> i64 {
28    let value = toml.get(key);
29
30    if value.is_none() {
31        eprintln!("Could not read toml field: \"{}\"", key);
32        0
33    } else {
34        value.unwrap().as_integer().unwrap_or_default()
35    }
36}
37
38impl InputData {
39    pub fn read(toml: &Value) -> InputData {
40        InputData {
41            username: get_str_or_default(toml, "username"),
42            password: get_str_or_default(toml, "password"),
43            dbname: get_str_or_default(toml, "dbname"),
44            host: get_str_or_default(toml, "host"),
45            port: get_int_or_default(toml, "port") as u16
46        }
47    }
48
49    pub fn postgres_url(&self) -> String {
50        format!(
51            "postgres://{}:{}@{}:{}/{}",
52            self.username,
53            self.password,
54            self.host,
55            self.port,
56            self.dbname
57        )
58    }
59}
60
61pub fn run_migration(toml_file_path: std::path::PathBuf, toml_table: String) {
62
63    let toml_file = toml_file_path;
64    let table_name = if !toml_table.is_empty() {
65        toml_table
66    } else {
67        String::from("database")
68    };
69
70    let toml_contents = match std::fs::read_to_string(toml_file) {
71        Ok(contents) => contents,
72        Err(e) => {
73            eprintln!("Error reading TOML file: {}", e);
74            return;
75        }
76    };
77
78    let toml_data: Value = match toml_contents.parse() {
79        Ok(data) => data,
80        Err(e) => {
81            eprintln!("Error parsing TOML file: {}", e);
82            return;
83        }
84    };
85
86    let table = get_toml_table(&table_name, &toml_data);
87
88    let input = InputData::read(table);
89
90    let db_url = input.postgres_url();
91
92    println!("Attempting to connect to {}", db_url);
93
94    let mut conn = PgConnection::establish(&db_url)
95    .expect("Unable to connect");
96
97    let migrations = FileBasedMigrations::find_migrations_directory()
98        .expect("Could not read migrations directory");
99
100    let mut harness = HarnessWithOutput::write_to_stdout(&mut conn);
101
102    harness.run_pending_migrations(migrations).expect("Couldn't run migrations");
103
104    println!("Successfully ran migrations")
105}
106
107
108pub fn get_toml_table<'a>(table_name: &'a str, toml_data: &'a Value) -> &'a Value{
109    if !table_name.is_empty() {
110        let table = toml_data.get(&table_name);
111        if table.is_none() {
112            eprintln!("Unable to find toml table: \"{}\"", &table_name);
113            std::process::abort()
114        }
115        
116        table.unwrap()
117    } else {
118        &toml_data
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125}