manaconf 0.2.0

a layered configuration library
Documentation
use std::env::{VarError, var};

use crate::helpers::join;
use crate::{Source, Key, Value};

/// A config value source that fetches values from environment variables
///
/// The source can be supplied with a prefix for environment variables
/// by using `with_prefix`.
///
/// Environment variable names, are thus then constructed as
/// `PREFIX_COMPONENT_COMPONENT_COMPONENT`
///
/// If there is no prefix then it's just `COMPONENT_COMPONENT_COMPONENT`
///
/// i.e. If the source has a prefix of `MYCONFIG` and you requested a value
/// with the key `database::connection_string`. The environment variable
/// that is looked up would be `MYCONFIG_DATABASE_CONNECTION_STRING`
///
/// It is worth noting, however, that this scheme may cause conflicts as
/// the key `database::connection_string` and `database::connection::string`
/// would result in the same environment variable `DATABASE_CONNECTION_STRING`.
pub struct EnvVarSource {
    prefix: Option<String>,
}

impl EnvVarSource {
    /// Creates an environment variable source
    pub fn new() -> Self {
        Self { prefix: None }
    }

    /// Creates an environment source with a given prefix to the environment
    /// variables name.
    pub fn with_prefix<T: Into<String>>(prefix: T) -> Self {
        Self::_with_prefix(prefix.into())
    }

    fn _with_prefix(mut prefix: String) -> Self {
        if !prefix.ends_with('_') {
            prefix.push('_');
        }

        Self {
            prefix: Some(prefix),
        }
    }
}

impl Source for EnvVarSource {
    type Error = VarError;

    fn get_value(&self, key: &Key) -> Result<Option<Value>, Self::Error> {
        let key_with_env_seperator = join(key.components(), "_");

        let mut env_key = self.prefix.clone().unwrap_or_default();
        env_key.push_str(&key_with_env_seperator);

        match var(env_key.to_uppercase()) {
            Ok(v) => Ok(Some(Value::String(v))),
            Err(std::env::VarError::NotPresent) => Ok(None),
            Err(e) => Err(e),
        }
    }
}