use crate::config::Config; use crate::StdResult; use postgres::{Client, Error, NoTls}; use std::convert::TryFrom; pub struct DatabaseConnection { client: Client, } impl TryFrom<&Config> for DatabaseConnection { type Error = Box; fn try_from(config: &Config) -> Result { DatabaseConnection::open(&config.database_connection_string()?) } } impl DatabaseConnection { pub fn open(connection_string: &str) -> StdResult { let client = Client::connect(connection_string, NoTls)?; Ok(DatabaseConnection { client }) } } pub fn is_migrations_table_not_found(e: &Error) -> bool { e.to_string() .contains(r#"relation "migrations" does not exist"#) } impl DatabaseConnection { pub fn apply_sql(&mut self, sql_content: &str) -> Result<(), Error> { self.client.batch_execute(sql_content) } pub fn applied_migration_names(&mut self) -> Result, Error> { let res = self .client .query("SELECT name FROM migrations ORDER BY id DESC", &[]) .or_else(|e| { if is_migrations_table_not_found(&e) { Ok(Vec::new()) } else { Err(e) } })?; Ok(res.into_iter().map(|row| row.get(0)).collect()) } pub fn create_migrations_table(&mut self) -> Result<(), Error> { self.apply_sql( r#"CREATE TABLE IF NOT EXISTS migrations ( id serial PRIMARY KEY, name text NOT NULL UNIQUE )"#, ) } pub fn insert_migration_info(&mut self, name: &str) -> Result { self.client .execute("INSERT INTO migrations (name) VALUES ($1)", &[&name]) } pub fn delete_migration_info(&mut self, name: &str) -> Result { self.client .execute("DELETE FROM migrations WHERE name = $1", &[&name]) } }