manaconf/sources/env_var_source.rs
1use std::env::{VarError, var};
2
3use crate::helpers::join;
4use crate::{Source, Key, Value};
5
6/// A config value source that fetches values from environment variables
7///
8/// The source can be supplied with a prefix for environment variables
9/// by using `with_prefix`.
10///
11/// Environment variable names, are thus then constructed as
12/// `PREFIX_COMPONENT_COMPONENT_COMPONENT`
13///
14/// If there is no prefix then it's just `COMPONENT_COMPONENT_COMPONENT`
15///
16/// i.e. If the source has a prefix of `MYCONFIG` and you requested a value
17/// with the key `database::connection_string`. The environment variable
18/// that is looked up would be `MYCONFIG_DATABASE_CONNECTION_STRING`
19///
20/// It is worth noting, however, that this scheme may cause conflicts as
21/// the key `database::connection_string` and `database::connection::string`
22/// would result in the same environment variable `DATABASE_CONNECTION_STRING`.
23pub struct EnvVarSource {
24 prefix: Option<String>,
25}
26
27impl EnvVarSource {
28 /// Creates an environment variable source
29 pub fn new() -> Self {
30 Self { prefix: None }
31 }
32
33 /// Creates an environment source with a given prefix to the environment
34 /// variables name.
35 pub fn with_prefix<T: Into<String>>(prefix: T) -> Self {
36 Self::_with_prefix(prefix.into())
37 }
38
39 fn _with_prefix(mut prefix: String) -> Self {
40 if !prefix.ends_with('_') {
41 prefix.push('_');
42 }
43
44 Self {
45 prefix: Some(prefix),
46 }
47 }
48}
49
50impl Source for EnvVarSource {
51 type Error = VarError;
52
53 fn get_value(&self, key: &Key) -> Result<Option<Value>, Self::Error> {
54 let key_with_env_seperator = join(key.components(), "_");
55
56 let mut env_key = self.prefix.clone().unwrap_or_default();
57 env_key.push_str(&key_with_env_seperator);
58
59 match var(env_key.to_uppercase()) {
60 Ok(v) => Ok(Some(Value::String(v))),
61 Err(std::env::VarError::NotPresent) => Ok(None),
62 Err(e) => Err(e),
63 }
64 }
65}