1use std::collections::HashMap;
2use serde_derive::Deserialize;
3use std::fs;
4use std::path::Path;
5use toml;
6use envconfig::Envconfig;
7use toml::{Table, Value};
8
9pub const CONFIG_FILENAME: &str = "config.toml";
10const DEFAULT_TMP_DIR: &str = "/tmp";
11
12#[derive(Deserialize, Debug)]
13pub struct Config {
14 pub alfred: AlfredConfig,
15 module: HashMap<String, String>
16}
17
18impl Config {
19 pub fn read(module_name: Option<&str>) -> Self {
20 let alfred = Self::read_alfred_config();
21 let module = module_name.map_or_else(HashMap::new, Self::read_module_config);
22 Self { alfred, module }
23 }
24
25 fn read_module_config(module_name: &str) -> HashMap<String, String> {
26 let contents = fs::read_to_string(CONFIG_FILENAME).expect("Could not read file");
27 let table: Table = contents.parse().expect("Could not parse toml file");
28 table.get(&module_name.to_string())
29 .and_then(Value::as_table)
30 .map_or_else(HashMap::new, |module_config| module_config
31 .iter()
32 .map(|(k, v)| (k.to_string(), v.as_str().unwrap_or("").to_string()))
33 .collect())
34 }
35
36 fn read_alfred_config() -> AlfredConfig {
37 let from_env = EnvConfig::from_env();
38 let from_file_config = FromFileConfig::read();
39 let url = from_env.alfred.url.unwrap_or(from_file_config.alfred.url);
40 let pub_port = from_env.alfred.pub_port.unwrap_or(from_file_config.alfred.pub_port);
41 let sub_port = from_env.alfred.sub_port.unwrap_or(from_file_config.alfred.sub_port);
42 let tmp_dir = from_env.alfred.tmp_dir
43 .or(from_file_config.alfred.tmp_dir)
44 .unwrap_or_else(|| DEFAULT_TMP_DIR.to_string());
45 AlfredConfig { url, pub_port, sub_port, tmp_dir, modules: from_file_config.alfred.modules }
46 }
47
48 pub fn get_alfred_pub_url(&self) -> String {
49 format!("{}:{}", self.alfred.url, self.alfred.pub_port)
50 }
51 pub fn get_alfred_sub_url(&self) -> String {
52 format!("{}:{}", self.alfred.url, self.alfred.sub_port)
53 }
54 pub fn get_module_value(&self, key: &str) -> Option<String> {
55 self.module.get(key).cloned()
56 }
57}
58
59#[derive(Deserialize, Debug)]
60pub struct AlfredConfig {
61 pub url: String,
62 pub pub_port: u32,
63 pub sub_port: u32,
64 pub tmp_dir: String,
65 pub modules: Vec<String>
66}
67
68#[derive(Deserialize, Debug)]
69struct FromFileConfig {
70 alfred: FromFileAlfredConfig
71}
72
73impl FromFileConfig {
74
75 fn get_config_filename() -> String {
76 std::env::var("ALFRED_CONFIG")
77 .ok()
78 .and_then(|path| Path::new(&path.as_str()).exists().then_some(path))
79 .unwrap_or_else(|| CONFIG_FILENAME.to_string())
80 }
81
82 fn read() -> Self {
83 let contents = fs::read_to_string(Self::get_config_filename()).expect("Could not read file");
84 toml::from_str(&contents).expect("Unable to load data")
85 }
86}
87
88#[derive(Deserialize, Debug)]
89struct FromFileAlfredConfig {
90 url: String,
91 pub_port: u32,
92 sub_port: u32,
93 tmp_dir: Option<String>,
94 #[serde(default)]
95 modules: Vec<String>
96}
97
98#[derive(Deserialize, Debug)]
99struct EnvConfig {
100 alfred: EnvAlfredConfig,
101}
102impl EnvConfig {
103
104 fn from_env() -> Self {
105 Self {
106 alfred: EnvAlfredConfig::init_from_env().expect("Error during initialization using env variable"),
107 }
108 }
109}
110
111#[derive(Deserialize, Debug, Envconfig)]
112struct EnvAlfredConfig {
113 #[envconfig(from = "ALFRED_URL")]
114 url: Option<String>,
115 #[envconfig(from = "ALFRED_PUB_PORT")]
116 pub_port: Option<u32>,
117 #[envconfig(from = "ALFRED_SUB_PORT")]
118 sub_port: Option<u32>,
119 #[envconfig(from = "ALFRED_TMP_DIR")]
120 tmp_dir: Option<String>
121}