use std::{
process,
sync::{Arc, Mutex, Weak},
};
use url::Url;
use crate::Postgres;
#[derive(Debug)]
pub enum DbInstance {
Local {
_arc: Arc<Postgres>,
url: Url,
},
External {
url: Url,
superuser_url: Url,
},
}
impl DbInstance {
pub fn as_str(&self) -> &str {
match self {
DbInstance::Local { url, .. } => url.as_str(),
DbInstance::External { url, .. } => url.as_str(),
}
}
pub fn as_url(&self) -> &Url {
match self {
DbInstance::Local { url, .. } => url,
DbInstance::External { url, .. } => url,
}
}
}
impl AsRef<str> for DbInstance {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Drop for DbInstance {
fn drop(&mut self) {
if let DbInstance::External { url, superuser_url } = self {
let db_name = url.path().trim_start_matches('/');
let db_user = url.username();
let psql_binary = which::which("psql").unwrap_or_else(|_| "psql".into());
let run_cleanup_sql = |sql: &str| {
let username = superuser_url.username();
let password = superuser_url.password().unwrap_or_default();
let host = superuser_url.host_str().unwrap_or("localhost");
let port = superuser_url.port().unwrap_or(5432);
let _ = process::Command::new(&psql_binary)
.arg("-h")
.arg(host)
.arg("-p")
.arg(port.to_string())
.arg("-U")
.arg(username)
.arg("-d")
.arg("postgres")
.arg("-c")
.arg(sql)
.env("PGPASSWORD", password)
.output();
};
run_cleanup_sql(&format!(
"DROP DATABASE IF EXISTS {};",
crate::escape_ident(db_name)
));
run_cleanup_sql(&format!(
"DROP ROLE IF EXISTS {};",
crate::escape_ident(db_user)
));
}
}
}
pub fn db_fixture() -> DbInstance {
if let Some(external_url) = crate::parse_external_test_url().expect("invalid PGDB_TESTS_URL") {
let url =
crate::create_fixture_db(&external_url).expect("failed to create external fixture DB");
return DbInstance::External {
url,
superuser_url: external_url,
};
}
static DB: Mutex<Weak<Postgres>> = Mutex::new(Weak::new());
let pg = {
let mut guard = DB.lock().expect("lock poisoned");
if let Some(arc) = guard.upgrade() {
arc
} else {
let arc = Arc::new(
Postgres::build()
.start()
.expect("failed to start global postgres DB"),
);
*guard = Arc::downgrade(&arc);
arc
}
};
let url =
crate::create_fixture_db(pg.superuser_url()).expect("failed to create local fixture DB");
DbInstance::Local { _arc: pg, url }
}