use crate::nesting;
use serde_json::Value;
use std::collections::HashMap;
pub fn with_prefix(prefix: &str) -> Value {
from_vars(prefix, std::env::vars())
}
fn from_vars(prefix: &str, vars: impl Iterator<Item = (String, String)>) -> Value {
let needle = format!("{prefix}_");
let mut flat: HashMap<String, Value> = HashMap::new();
for (key, value) in vars {
if let Some(rest) = key.strip_prefix(&needle) {
let path = rest.replace("__", ".").to_lowercase();
let parsed = serde_json::from_str(&value).unwrap_or(Value::String(value));
flat.insert(path, parsed);
}
}
nesting::nest(flat)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn vars(pairs: &[(&str, &str)]) -> impl Iterator<Item = (String, String)> {
pairs
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect::<Vec<_>>()
.into_iter()
}
#[test]
fn non_matching_prefix_ignored() {
let result = from_vars("APP", vars(&[("OTHER_FOO", "bar")]));
assert_eq!(result, json!({}));
}
#[test]
fn matching_prefix_picked_up() {
let result = from_vars("APP", vars(&[("APP_FOO", "bar")]));
assert_eq!(result["foo"], json!("bar"));
}
#[test]
fn key_is_lowercased() {
let result = from_vars("APP", vars(&[("APP_FOO_BAR", "x")]));
assert_eq!(result["foo_bar"], json!("x"));
}
#[test]
fn double_underscore_becomes_nesting() {
let result = from_vars("APP", vars(&[("APP_DATABASE__HOST", "localhost")]));
assert_eq!(result["database"]["host"], json!("localhost"));
}
#[test]
fn json_number_parsed() {
let result = from_vars("APP", vars(&[("APP_PORT", "8080")]));
assert_eq!(result["port"], json!(8080));
}
#[test]
fn json_bool_parsed() {
let result = from_vars("APP", vars(&[("APP_DEBUG", "true")]));
assert_eq!(result["debug"], json!(true));
}
#[test]
fn non_json_kept_as_string() {
let result = from_vars("APP", vars(&[("APP_NAME", "my app")]));
assert_eq!(result["name"], json!("my app"));
}
#[test]
fn multiple_vars() {
let result = from_vars(
"APP",
vars(&[
("APP_HOST", "localhost"),
("APP_PORT", "9000"),
("OTHER_X", "ignored"),
]),
);
assert_eq!(result["host"], json!("localhost"));
assert_eq!(result["port"], json!(9000));
assert_eq!(result.get("x"), None);
}
}