use std::collections::HashMap;
use std::env;
use std::path::PathBuf;
use std::env::{current_dir, home_dir};
use value::Value;
use error::Error;
#[derive(Debug)]
pub struct Inner {
pub(crate) config_files: Vec<(String, Value)>,
pub(crate) env_vars: Value,
pub(crate) cli_flags: Value,
}
unsafe impl Send for Inner {}
impl Inner {
pub fn load(application_name: &str) -> Result<Self, Error> {
Ok(Self {
config_files: Self::gather_config_files(application_name)?,
env_vars: Self::gather_env_vars(application_name)?,
cli_flags: Self::gather_cli_flags(application_name)?,
})
}
pub fn gather_config_files(application_name: &str) -> Result<Vec<(String, Value)>, Error> {
let mut paths = Vec::new();
let mut cwd_path = current_dir().unwrap();
if let Some(home_path) = home_dir() {
if !cwd_path.starts_with(&home_path) {
paths.push(home_path.join(PathBuf::from(format!(".{}rc", application_name))));
}
paths.push(home_path.join(PathBuf::from(format!(".{}/config", application_name))));
paths.push(home_path.join(PathBuf::from(format!(".config/{}", application_name))));
paths.push(home_path.join(PathBuf::from(
format!(".config/{}/config", application_name),
)));
}
paths.push(PathBuf::from(format!("/etc/{}rc", application_name)));
paths.push(PathBuf::from(format!("/etc/{}/config", application_name)));
paths.push(PathBuf::from(
format!("/usr/local/etc/{}rc", application_name),
));
paths.push(PathBuf::from(
format!("/usr/local/etc/{}/config", application_name),
));
while let Some(_) = cwd_path.file_name() {
paths.push(cwd_path.join(PathBuf::from(format!(".{}rc", application_name))));
cwd_path.pop();
}
paths.push(PathBuf::from(format!("/.{}rc", application_name)));
let mut config_files = Vec::new();
for path in paths {
if !path.is_file() {
continue;
}
let path_string = path.to_str().unwrap().to_string();
let config_data = Value::from_path(&path).map_err(|e| Error::from(e))?;
config_files.push((path_string, config_data));
}
Ok(config_files)
}
pub fn gather_env_vars(application_name: &str) -> Result<Value, Error> {
let prelude_len = application_name.len() + 2;
let mut root = Value::from(HashMap::new());
let env_vars: Vec<_> = env::vars().filter(|&(ref k, _)| {
k.len() >= prelude_len &&
format!("{}__", application_name).to_uppercase() ==
k[0..prelude_len].to_uppercase()
}).collect();
for (ref path, ref value) in env_vars {
let path = path[prelude_len..]
.split("__")
.map(|p| p.to_lowercase())
.collect::<Vec<String>>()
.join(".");
root.set(&path, Value::from(value.to_owned()));
}
Ok(root)
}
pub fn gather_cli_flags(_: &str) -> Result<Value, Error> {
let mut root = Value::from(HashMap::new());
let cli_flags: Vec<_> = env::args()
.filter(|a| &a[0..2] == "--" && a.contains("="))
.map(|a| {
let split_index = a.find("=").unwrap();
let (flag, value) = a.split_at(split_index);
(flag[2..].to_string(), value[1..].to_string())
})
.map(|(path, value)| {
let path = path
.split("--")
.map(|c| c.replace("-", "_"))
.collect::<Vec<String>>()
.join(".");
(path, value)
})
.collect();
for (ref path, ref value) in cli_flags {
root.set(&path, Value::from(value.to_owned()));
}
Ok(root)
}
}