1use std::str::FromStr;
4
5pub fn require(key: &str) -> Result<String, String> {
7 std::env::var(key).map_err(|_| format!("Missing required env var: {key}"))
8}
9
10pub fn string_or(key: &str, default: &str) -> String {
12 std::env::var(key).unwrap_or_else(|_| default.to_string())
13}
14
15pub fn parse_or<T: FromStr>(key: &str, default: T) -> T {
17 parse_from(std::env::var(key).ok(), default)
18}
19
20pub fn bool_or(key: &str, default: bool) -> bool {
22 bool_from(std::env::var(key).ok(), default)
23}
24
25fn parse_from<T: FromStr>(value: Option<String>, default: T) -> T {
26 value.and_then(|v| v.parse().ok()).unwrap_or(default)
27}
28
29fn bool_from(value: Option<String>, default: bool) -> bool {
30 value.map(|v| v == "true" || v == "1").unwrap_or(default)
31}
32
33#[cfg(test)]
34mod tests {
35 use super::*;
36
37 #[test]
38 fn test_require_missing() {
39 let err = require("RIVER_CORE_TEST_UNSET").unwrap_err();
40 assert!(err.contains("RIVER_CORE_TEST_UNSET"));
41 }
42
43 #[test]
44 fn test_string_or_unset() {
45 assert_eq!(string_or("RIVER_CORE_TEST_UNSET", "fallback"), "fallback");
46 }
47
48 #[test]
49 fn test_parse_from_garbage_falls_back() {
50 assert_eq!(parse_from(Some("not-a-number".to_string()), 7u64), 7);
51 }
52
53 #[test]
54 fn test_parse_from_valid() {
55 assert_eq!(parse_from(Some("12".to_string()), 7u64), 12);
56 assert_eq!(parse_from::<i64>(None, 42), 42);
57 }
58
59 #[test]
60 fn test_bool_from() {
61 assert!(bool_from(Some("1".to_string()), false));
62 assert!(bool_from(Some("true".to_string()), false));
63 assert!(!bool_from(Some("no".to_string()), true));
64 assert!(bool_from(None, true));
65 }
66}