pub mod query_builder;
pub mod sql;
use std::env::var;
use tokio::time::Duration;
pub fn parse_env_reference(uri: &str) -> Option<String> {
uri.strip_prefix("${")
.and_then(|value| value.strip_suffix('}'))
.map(str::to_string)
}
pub fn resolve_postgres_uri(uri: &str) -> String {
if let Some(env_name) = parse_env_reference(uri) {
var(env_name).unwrap_or_else(|_| uri.to_string())
} else {
uri.to_string()
}
}
pub fn describe_postgres_uri_problem(uri: &str) -> Option<String> {
let trimmed = uri.trim();
if trimmed.is_empty() {
return Some("connection string is empty".to_string());
}
let resolved = resolve_postgres_uri(trimmed);
if resolved.trim().is_empty() {
if let Some(env_name) = parse_env_reference(trimmed) {
return Some(format!(
"environment variable `{env_name}` is unset, empty, or only whitespace"
));
}
return Some("connection string resolves to an empty value".to_string());
}
if let Some(env_name) = parse_env_reference(trimmed) {
if resolved == trimmed {
return Some(format!("environment variable `{env_name}` is not set"));
}
}
if !resolved.starts_with("postgres://") && !resolved.starts_with("postgresql://") {
return Some(
"expected postgres:// or postgresql:// after resolving env references (see config.yaml)"
.to_string(),
);
}
None
}
pub fn parse_secs_or_default(value: Option<&String>, default_secs: u64) -> Duration {
Duration::from_secs(
value
.and_then(|v| v.parse::<u64>().ok())
.unwrap_or(default_secs),
)
}
pub fn parse_usize(value: &str) -> Option<usize> {
value.parse().ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn describe_postgres_uri_problem_empty() {
assert_eq!(
describe_postgres_uri_problem(" "),
Some("connection string is empty".to_string())
);
}
#[test]
fn describe_postgres_uri_problem_unresolved_env_placeholder() {
let name = "ATHENA_PARSER_TEST_URI_UNSET_7E4B";
let template = format!("${{{name}}}");
let msg = describe_postgres_uri_problem(&template).expect("expected problem");
assert!(
msg.contains(name) && msg.contains("not set"),
"unexpected message: {msg}"
);
}
#[test]
fn describe_postgres_uri_problem_ok_postgres_url() {
assert_eq!(
describe_postgres_uri_problem("postgres://user:pass@localhost/db"),
None
);
}
#[test]
fn describe_postgres_uri_problem_bad_scheme() {
let msg = describe_postgres_uri_problem("mysql://localhost/db").expect("expected problem");
assert!(
msg.contains("postgres://") || msg.contains("postgresql://"),
"unexpected message: {msg}"
);
}
}