anor_common/utils/
config.rs1use 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}