use std::{ default::default, env, ffi::{OsStr, OsString}, fs::File, io::Read, }; // from https://doc.rust-lang.org/std/ffi/struct.OsString.html fn concat_os_strings(a: &OsStr, b: &OsStr) -> OsString { let mut ret = OsString::with_capacity(a.len() + b.len()); // This will allocate ret.push(a); // This will not allocate further ret.push(b); // This will not allocate further ret } /// Check for an environment variable named for the given key with _FILE /// appended to the end. If that exists, return the contents of the file, or /// panic if it doesn't exist. If the `"${key}_FILE"` environment variable is /// not set, return the value of the environment variable set by the given key, /// or `None` if it's not set. /// /// Panics: /// - if the given file variable doesn't exist /// - if there's an error reading the specified file /// - if the environment variable string contains invalid unicode. fn get_env_var_or_file>(key: A) -> Option { let key = key.as_ref(); let file_key = concat_os_strings(key, "_FILE".as_ref()); if let Some(path) = env::var_os(file_key) { // open the file and read it let mut file = File::open(&path).unwrap_or_else(|_| panic!("no such file at {path:?}")); let mut val: String = default(); file.read_to_string(&mut val) .unwrap_or_else(|_| panic!("reading file at {path:?}")); Some(val) } else { env::var_os(key).map(|val| { val.to_str() .expect(&format!("value for ${key:?} contains invalid unicode")) .to_string() }) } } /// Connect to the database using environment variables for configuration. /// Panics on any failure. pub(crate) fn connection_url() -> String { let user = get_env_var_or_file("POSTGRES_USER").expect("$POSTGRES_USER"); let password = get_env_var_or_file("POSTGRES_PASSWORD").expect("$POSTGRES_PASSWORD"); let db = get_env_var_or_file("POSTGRES_DB").expect("$POSTGRES_DB"); let host = get_env_var_or_file("POSTGRES_HOST").unwrap_or_else(|| "localhost".into()); let port = get_env_var_or_file("POSTGRES_PORT") .map(|port| { port.parse::() .expect("$POSTGRES_PORT is not a valid port number") }) .unwrap_or(5432_u16); format!("postgres://{user}:{password}@{host}:{port}/{db}") }