anor_common/utils/
config.rs

1use std::collections::HashMap;
2use std::io::Read;
3use std::net::{IpAddr, SocketAddr};
4use std::path::PathBuf;
5use std::sync::Arc;
6
7use super::{build_profile, envsubst};
8
9const DEFAULT_CONFIG_FILENAME_RELEASE: &str = "anor-config.yaml";
10const DEFAULT_CONFIG_FILENAME_DEBUG: &str = "anor-config.debug";
11const DEFAULT_CONFIG_FILENAME_TEST: &str = "anor-config.test";
12
13const DEFAULT_STORAGE_DATA_PATH: &str = "/var/anor";
14
15const DEFAULT_API_SERVICE_LISTEN_ADDRESS: &str = "127.0.0.1";
16const DEFAULT_API_SERVICE_LISTEN_PORT: u16 = 7311;
17const DEFAULT_API_SERVICE_ENABLED: bool = false;
18
19const DEFAULT_HTTP_SERVICE_LISTEN_ADDRESS: &str = "127.0.0.1";
20const DEFAULT_HTTP_SERVICE_LISTEN_PORT: u16 = 8181;
21const DEFAULT_HTTP_SERVICE_ENABLED: bool = false;
22
23const DEFAULT_REMOTE_NODE: &str = "127.0.0.1:9191";
24
25#[derive(Debug)]
26pub struct Config {
27    pub storage: Option<StorageConfig>,
28    pub api: Option<ApiConfig>,
29    pub http: Option<HttpConfig>,
30    pub remote: Option<RemoteConfig>,
31}
32
33#[derive(Debug)]
34pub struct StorageConfig {
35    pub data_path: PathBuf,
36}
37
38#[derive(Debug)]
39pub struct ApiConfig {
40    pub listen_on: Vec<SocketAddr>,
41    pub enabled: bool,
42}
43
44#[derive(Debug)]
45pub struct HttpConfig {
46    pub listen_on: Vec<SocketAddr>,
47    pub enabled: bool,
48}
49
50#[derive(Debug)]
51pub struct RemoteConfig {
52    pub nodes: Vec<SocketAddr>,
53}
54
55pub fn get_config() -> Arc<Config> {
56    let config_filename = get_config_filename();
57    let mut config_file = std::fs::File::open(config_filename)
58        .unwrap_or_else(|_| panic!("Could not open {} file.", config_filename));
59
60    let mut config_content = String::new();
61    if let Err(err) = config_file.read_to_string(&mut config_content) {
62        log::error!("{}", err);
63        panic!("{}", err);
64    }
65
66    let config_substituted = envsubst::substitute(&config_content);
67
68    let config_map: HashMap<String, HashMap<String, String>> =
69        serde_yaml::from_str(&config_substituted)
70            .unwrap_or_else(|_| panic!("Could not parse {} file.", config_filename));
71
72    if log::log_enabled!(log::Level::Trace) {
73        log::trace!("loaded config:\n{:#?}", config_map);
74    }
75
76    let mut config = Config {
77        storage: None,
78        api: None,
79        http: None,
80        remote: None,
81    };
82
83    let map_key = "storage";
84    if config_map.contains_key(map_key) {
85        let config_node = &config_map[map_key];
86        let data_path = parse_storage_path(config_node);
87        config.storage = Some(StorageConfig { data_path });
88    }
89
90    let map_key = "api";
91    if config_map.contains_key(map_key) {
92        let config_node = &config_map[map_key];
93        let listen_on = parse_listen_on(
94            config_node,
95            DEFAULT_API_SERVICE_LISTEN_ADDRESS,
96            DEFAULT_API_SERVICE_LISTEN_PORT,
97        );
98        let enabled = parse_enabled(config_node).unwrap_or(DEFAULT_API_SERVICE_ENABLED);
99        config.api = Some(ApiConfig { listen_on, enabled });
100    }
101
102    let map_key = "http";
103    if config_map.contains_key(map_key) {
104        let config_node = &config_map[map_key];
105        let listen_on = parse_listen_on(
106            config_node,
107            DEFAULT_HTTP_SERVICE_LISTEN_ADDRESS,
108            DEFAULT_HTTP_SERVICE_LISTEN_PORT,
109        );
110        let enabled = parse_enabled(config_node).unwrap_or(DEFAULT_HTTP_SERVICE_ENABLED);
111        config.http = Some(HttpConfig { listen_on, enabled });
112    }
113
114    let map_key = "remote";
115    if config_map.contains_key(map_key) {
116        let config_node = &config_map[map_key];
117        let remote = parse_remote(config_node);
118        config.remote = Some(remote);
119    }
120
121    if log::log_enabled!(log::Level::Debug) {
122        log::debug!("parsed config:\n{:#?}", config);
123    }
124
125    Arc::new(config)
126}
127
128fn parse_listen_on(
129    node: &HashMap<String, String>,
130    default_listen_address: &str,
131    default_listen_port: u16,
132) -> Vec<SocketAddr> {
133    let node_key = "listen_addresses";
134    let listen_addresses = if node.contains_key(node_key) {
135        node[node_key]
136            .split(',')
137            .map(|s| s.trim())
138            .collect::<Vec<_>>()
139    } else {
140        vec![default_listen_address]
141    };
142
143    if log::log_enabled!(log::Level::Trace) {
144        log::trace!("config: listen_addresses: {:?}", listen_addresses);
145    }
146
147    let node_key = "listen_port";
148    let port = if node.contains_key(node_key) {
149        node[node_key].parse().unwrap()
150    } else {
151        default_listen_port
152    };
153
154    if log::log_enabled!(log::Level::Trace) {
155        log::trace!("config: listen_port: {}", port);
156    }
157
158    let mut listen_on = Vec::<SocketAddr>::with_capacity(listen_addresses.len());
159    for listen_addres in listen_addresses {
160        let ip_address: IpAddr = listen_addres.parse().unwrap();
161        let socket_addres = SocketAddr::new(ip_address, port);
162        listen_on.push(socket_addres);
163    }
164
165    if log::log_enabled!(log::Level::Trace) {
166        log::trace!("parsed: listen_on: {:?}", listen_on);
167    }
168
169    listen_on
170}
171
172fn parse_storage_path(node: &HashMap<String, String>) -> PathBuf {
173    let node_key = "data_path";
174    let storage_path = if node.contains_key(node_key) {
175        node[node_key].parse().unwrap()
176    } else {
177        String::from(DEFAULT_STORAGE_DATA_PATH)
178    };
179
180    PathBuf::from(storage_path)
181}
182
183fn parse_remote(node: &HashMap<String, String>) -> RemoteConfig {
184    let node_key = "nodes";
185    let remote_nodes = if node.contains_key(node_key) {
186        node[node_key]
187            .split(',')
188            .map(|s| s.trim())
189            .collect::<Vec<_>>()
190    } else {
191        vec![DEFAULT_REMOTE_NODE]
192    };
193
194    if log::log_enabled!(log::Level::Trace) {
195        log::trace!("config: remote nodes: {:?}", remote_nodes);
196    }
197
198    let mut nodes = Vec::<SocketAddr>::with_capacity(remote_nodes.len());
199    for node in remote_nodes {
200        let socket_addr: SocketAddr = node.parse().unwrap();
201        nodes.push(socket_addr);
202    }
203
204    if log::log_enabled!(log::Level::Trace) {
205        log::trace!("parsed: remote nodes: {:?}", nodes);
206    }
207
208    RemoteConfig { nodes }
209}
210
211fn parse_enabled(node: &HashMap<String, String>) -> Option<bool> {
212    let node_key = "enabled";
213    if node.contains_key(node_key) {
214        Some(node[node_key].parse().unwrap())
215    } else {
216        None
217    }
218}
219
220fn get_config_filename() -> &'static str {
221    if build_profile::debug_mode() {
222        if build_profile::is_cargo_test() {
223            DEFAULT_CONFIG_FILENAME_TEST
224        } else {
225            DEFAULT_CONFIG_FILENAME_DEBUG
226        }
227    } else {
228        DEFAULT_CONFIG_FILENAME_RELEASE
229    }
230}
231
232#[cfg(test)]
233mod test {
234    use super::*;
235
236    #[test]
237    fn config_file_test() {
238        assert!(build_profile::is_cargo_test());
239        assert_eq!(get_config_filename(), DEFAULT_CONFIG_FILENAME_TEST);
240    }
241
242    #[test]
243    fn config_storage_test() {
244        let config = get_config();
245        assert!(config.storage.is_some());
246
247        let storage = config.storage.as_ref().unwrap();
248        let data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
249            .join("target")
250            .join("tmp")
251            .join("anor");
252        assert_eq!(storage.data_path, data_path);
253    }
254
255    #[test]
256    fn config_api_test() {
257        let config = get_config();
258        assert!(config.api.is_some());
259
260        let api = config.api.as_ref().unwrap();
261        assert_eq!(api.listen_on.len(), 1);
262        assert_eq!(api.listen_on[0], "127.0.0.1:9191".parse().unwrap());
263        assert!(api.enabled);
264    }
265
266    #[test]
267    fn config_http_test() {
268        let config = get_config();
269        assert!(config.http.is_some());
270
271        let http = config.http.as_ref().unwrap();
272        assert_eq!(http.listen_on.len(), 1);
273        assert_eq!(http.listen_on[0], "127.0.0.1:8181".parse().unwrap());
274        assert!(http.enabled);
275    }
276
277    #[test]
278    fn config_remote_test() {
279        let config = get_config();
280        assert!(config.remote.is_some());
281
282        let remote = config.remote.as_ref().unwrap();
283        assert_eq!(remote.nodes.len(), 1);
284        assert_eq!(remote.nodes[0], "127.0.0.1:9191".parse().unwrap());
285    }
286}