//! Reads of the runtime-tunable `config` table. //! //! Each handler used to keep a small local copy of these helpers; consolidating them //! here means one place to add a parser, one place to mock for tests, and one place to //! find when a key changes. New keys do not require code changes — they're picked up //! the next time someone calls `get_*`. //! //! Values are read with a default fallback so the app still starts if a key is missing //! (e.g. during a migration window). Production seeds keys via migrations 005 and 009. use sqlx::PgPool; async fn fetch_raw(pool: &PgPool, key: &str) -> Option { sqlx::query_as::<_, (String,)>("SELECT value FROM config WHERE key = $1") .bind(key) .fetch_optional(pool) .await .ok() .flatten() .map(|(v,)| v) } pub async fn get_str(pool: &PgPool, key: &str, default: &str) -> String { fetch_raw(pool, key).await.unwrap_or_else(|| default.to_string()) } pub async fn get_i64(pool: &PgPool, key: &str, default: i64) -> i64 { fetch_raw(pool, key).await.and_then(|v| v.parse().ok()).unwrap_or(default) } pub async fn get_usize(pool: &PgPool, key: &str, default: usize) -> usize { fetch_raw(pool, key).await.and_then(|v| v.parse().ok()).unwrap_or(default) } pub async fn get_f64(pool: &PgPool, key: &str, default: f64) -> f64 { fetch_raw(pool, key).await.and_then(|v| v.parse().ok()).unwrap_or(default) } /// Parses common truthy spellings used by both the migration seeds and the admin form. /// Accepts `true/false`, `1/0`, `yes/no`, `on/off` — case-insensitive. Anything else /// returns `default`. pub async fn get_bool(pool: &PgPool, key: &str, default: bool) -> bool { let Some(raw) = fetch_raw(pool, key).await else { return default }; match raw.trim().to_ascii_lowercase().as_str() { "true" | "1" | "yes" | "on" => true, "false" | "0" | "no" | "off" => false, _ => default, } }