use super::{locate, toml_source};
use sqlx::postgres::PgConnectOptions;
use std::{env, fs};
pub(super) const KEYS: [&str; 5] = ["host", "port", "user", "password", "name"];
pub fn connect_options() -> Result<PgConnectOptions, String> {
if let Ok(url) = env::var("DATABASE_URL") {
return url
.parse()
.map_err(|error| format!("DATABASE_URL is not a valid Postgres URL: {error}"));
}
if let Some(values) = from_env() {
return build(&values, "DATABASE_* environment variables");
}
let path = locate::config_file().ok_or_else(|| {
"no database configuration: set DATABASE_URL, the DATABASE_* variables, \
or add a [database] section to rustyroad.toml"
.to_string()
})?;
let source = path.display().to_string();
let contents =
fs::read_to_string(&path).map_err(|error| format!("failed to read {source}: {error}"))?;
build(&toml_source::values(&contents, &source)?, &source)
}
fn from_env() -> Option<Vec<String>> {
KEYS.iter()
.map(|key| env::var(format!("DATABASE_{}", key.to_uppercase())).ok())
.collect()
}
pub(super) fn build(values: &[String], source: &str) -> Result<PgConnectOptions, String> {
let [host, port, user, password, name] = values else {
return Err(format!("{source} does not supply every database setting"));
};
let port = port
.parse::<u16>()
.map_err(|error| format!("{source} has an invalid database port '{port}': {error}"))?;
Ok(PgConnectOptions::new()
.host(host)
.port(port)
.username(user)
.password(password)
.database(name))
}