1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
pub use config::ConfigError;
use config::{Config, Value};
use serde::Deserialize;
use std::{collections::HashMap, fs};

pub trait Configurator {
    fn get<'de, T: Deserialize<'de>>(&'de self, key: &'de str) -> Result<T, ConfigError>;
    fn get_str(&self, key: &str) -> Result<String, ConfigError>;
    fn get_int(&self, key: &str) -> Result<i64, ConfigError>;
    fn get_float(&self, key: &str) -> Result<f64, ConfigError>;
    fn get_bool(&self, key: &str) -> Result<bool, ConfigError>;
    fn get_table(&self, key: &str) -> Result<HashMap<String, Value>, ConfigError>;
    fn get_array(&self, key: &str) -> Result<Vec<Value>, ConfigError>;
    fn try_into<'de, T: Deserialize<'de>>(self) -> Result<T, ConfigError>;
}

pub fn new(path: &str, prefix: &str) -> Result<impl Configurator, ConfigError> {
    let mut config_builder = Config::builder();
    let paths = fs::read_dir(path)
        .map_err(|_| ConfigError::Message(format!("Could not read directory at {}", path)))?
        .filter_map(|result| result.ok())
        .map(|de| de.path());
    for path in paths {
        config_builder = match path.as_path().to_str() {
            Some(s) => {
                if s.ends_with(".yaml") {
                    config_builder.add_source(config::File::with_name(s))
                } else {
                    config_builder
                }
            }
            None => config_builder,
        };
    }
    config_builder
        .add_source(config::Environment::with_prefix(prefix).separator("_"))
        .build()
}

impl Configurator for Config {
    fn get<'de, T: Deserialize<'de>>(&'de self, key: &'de str) -> Result<T, ConfigError> {
        Config::get(self, key)
    }
    fn get_str(&self, key: &str) -> Result<String, ConfigError> {
        Config::get_string(self, key)
    }
    fn get_int(&self, key: &str) -> Result<i64, ConfigError> {
        Config::get_int(self, key)
    }
    fn get_float(&self, key: &str) -> Result<f64, ConfigError> {
        Config::get_float(self, key)
    }
    fn get_bool(&self, key: &str) -> Result<bool, ConfigError> {
        Config::get_bool(self, key)
    }
    fn get_table(&self, key: &str) -> Result<HashMap<String, Value>, ConfigError> {
        Config::get_table(self, key)
    }
    fn get_array(&self, key: &str) -> Result<Vec<Value>, ConfigError> {
        Config::get_array(self, key)
    }
    fn try_into<'de, T: Deserialize<'de>>(self) -> Result<T, ConfigError> {
        Config::try_deserialize(self)
    }
}