1use std::fmt::Display;
4use std::path::PathBuf;
5use std::str::FromStr;
6
7use crate::{Error, Result};
8
9pub fn load_dotenv() -> Result<Option<PathBuf>> {
14 match dotenvy::dotenv() {
15 Ok(path) => Ok(Some(path)),
16 Err(err) if err.not_found() => Ok(None),
17 Err(err) => Err(err.into()),
18 }
19}
20
21pub fn var_or(key: &str, default: &str) -> String {
23 std::env::var(key).unwrap_or_else(|_| default.to_owned())
24}
25
26pub fn parse_or<T>(key: &str, default: T) -> Result<T>
28where
29 T: FromStr,
30 T::Err: Display,
31{
32 match std::env::var(key) {
33 Ok(value) => parse_value(key, &value),
34 Err(_) => Ok(default),
35 }
36}
37
38fn parse_value<T>(key: &str, value: &str) -> Result<T>
39where
40 T: FromStr,
41 T::Err: Display,
42{
43 value
44 .trim()
45 .parse()
46 .map_err(|err: T::Err| Error::InvalidEnv {
47 key: key.to_owned(),
48 value: value.to_owned(),
49 reason: err.to_string(),
50 })
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn parses_valid_values() {
59 assert_eq!(parse_value::<u16>("PORT", " 8080 ").unwrap(), 8080);
60 }
61
62 #[test]
63 fn reports_invalid_values() {
64 let err = parse_value::<u16>("PORT", "eighty").unwrap_err();
65 assert!(err.to_string().contains("PORT"), "{err}");
66 }
67
68 #[test]
69 fn falls_back_to_default_when_unset() {
70 assert_eq!(parse_or("SLAVE_TEST_SURELY_UNSET_VAR", 42u16).unwrap(), 42);
71 assert_eq!(var_or("SLAVE_TEST_SURELY_UNSET_VAR", "x"), "x");
72 }
73}