1use std::env;
48use std::error::Error;
49use std::fs;
50use std::path::Path;
51use std::sync::{Arc, Mutex};
52use once_cell::sync::Lazy;
53use serde_json::{Map, Value};
54
55struct ConfigSerde;
56
57static mut CONFIG_NAME: String = String::new();
58static mut CONFIG_PATH: String = String::new();
59static CONFIGS: Lazy<Arc<Mutex<Map<String, Value>>>> = Lazy::new(|| {
60 let m = Map::new();
61 Arc::new(Mutex::new(m))
62});
63
64impl ConfigSerde {
65 fn parse_value(value_ref: &Value) -> Value {
66 value_ref.clone()
67 }
68
69 fn read_config(config_path: &str) -> Result<Map<String, Value>, Box<dyn Error>> {
70 println!("reading file {}", config_path);
71 let config = fs::read_to_string(config_path)?;
72 let parsed: Map<String, Value> = serde_json::from_str(config.as_str())?;
73 let result = parsed
74 .into_iter()
75 .map(|(k, v)| (k, ConfigSerde::parse_value(&v)))
76 .collect();
77 Ok(result)
78 }
79}
80
81pub fn set_config_name(config_name: &str) {
88 unsafe { CONFIG_NAME = config_name.to_string(); }
89}
90
91pub fn add_config_path(path: &str) {
97 unsafe {
98 #[cfg(target_family = "unix")]
99 if path.ends_with("/") {
100 CONFIG_PATH = path.to_string();
101 } else {
102 CONFIG_PATH = path.to_string() + "/";
103 }
104 #[cfg(target_family = "windows")]
105 if path.ends_with("\\") {
106 CONFIG_PATH = path.to_string();
107 } else {
108 CONFIG_PATH = path.to_string() + "\\";
109 }
110 }
111}
112
113pub fn read_config() {
120 if !unsafe { CONFIG_NAME.is_empty() } {
121 let path_buf = env::current_exe().expect("Failed to get executable path");
122 let paths = fs::read_dir(path_buf.parent().unwrap()).unwrap();
123 let mut is_found:bool;
124 unsafe{
125 let file_path = CONFIG_PATH.to_owned() + &CONFIG_NAME;
126 let path = Path::new(&file_path);
127 is_found = path.exists() && path.is_file();
128 }
129 if !is_found {
130 for path in paths {
131 let path_str = path.unwrap().path();
132 let filename = path_str.file_name().unwrap().to_string_lossy();
133 unsafe {
134 if filename == CONFIG_NAME.to_string() {
135 #[cfg(target_family = "unix")]
136 {
137 CONFIG_PATH = path_str.clone().parent().unwrap().to_string_lossy().to_string() + "/";
138 }
139 #[cfg(target_family = "windows")]
140 {
141 CONFIG_PATH = path_str.clone().parent().unwrap().to_string_lossy().parse().unwrap() + "\\";
142 }
143 println!("file is found!!");
145 is_found = true;
146 break;
147 } }
151 }
152 }
153
154 if is_found {
155 init_lazy_configs(&mut CONFIGS.lock().unwrap());
156 } else {
157 println!("file is not found");
158 }
159 }
160}
161
162fn init_lazy_configs(input: &mut Map<String, Value>) {
163 let path = unsafe { CONFIG_PATH.to_string() + &CONFIG_NAME };
164 println!("init_lazy_configs path: {}", path);
165 match ConfigSerde::read_config(&path) {
166 Ok(configs) => {
167 for (k, v) in configs.iter() {
168 input.insert(k.clone(), v.clone()); }
170 }
171 Err(_e) => {
172 }
174 }
175 println!("configs: {:?}", input);
176}
177
178pub fn get_string(key: &str) -> Option<String> {
184 let configs = CONFIGS.lock().unwrap();
185 if let Some(value) = configs.get(key) {
186 value.as_str().map(|s| s.to_string())
187 } else {
188 None
189 }
190}
191
192pub fn get_string_array(key: &str) -> Option<Vec<String>> {
198 let configs = CONFIGS.lock().unwrap();
199 if let Some(value) = configs.get(key) {
200 if let Value::Array(arr) = value {
201 let mut string_array = Vec::new();
202 for element in arr {
203 if let Value::String(s) = element {
204 string_array.push(s.clone());
205 }
206 }
207 Some(string_array)
208 } else {
209 None
210 }
211 } else {
212 None
213 }
214}
215
216pub fn get_int64(key: &str) -> Option<i64> {
222 let configs = CONFIGS.lock().unwrap();
223 if let Some(value) = configs.get(key) {
224 match value {
225 Value::Number(n) => n.as_i64(),
226 _ => None,
227 }
228 } else {
229 None
230 }
231}
232
233pub fn get_int64_array(key: &str) -> Option<Vec<i64>> {
239 let configs = CONFIGS.lock().unwrap();
240 if let Some(value) = configs.get(key) {
241 if let Value::Array(arr) = value {
242 let mut int64_array = Vec::new();
243 for element in arr {
244 if let Value::Number(n) = element {
245 if let Some(int_value) = n.as_i64() {
246 int64_array.push(int_value);
247 }
248 }
249 }
250 Some(int64_array)
251 } else {
252 None
253 }
254 } else {
255 None
256 }
257}
258
259pub fn get_i32(key: &str) -> Option<i32> {
265 let configs = CONFIGS.lock().unwrap();
266 if let Some(value) = configs.get(key) {
267 match value {
268 Value::Number(n) => n.as_i64().map(|n| n as i32),
269 _ => None,
270 }
271 } else {
272 None
273 }
274}
275
276pub fn get_i16(key: &str) -> Option<i16> {
282 let configs = CONFIGS.lock().unwrap();
283 if let Some(value) = configs.get(key) {
284 match value {
285 Value::Number(n) => n.as_i64().map(|n| n as i16),
286 _ => None,
287 }
288 } else {
289 None
290 }
291}
292
293pub fn get_int8(key: &str) -> Option<i8> {
299 let configs = CONFIGS.lock().unwrap();
300 if let Some(value) = configs.get(key) {
301 match value {
302 Value::Number(n) => n.as_i64().map(|n| n as i8),
303 _ => None,
304 }
305 } else {
306 None
307 }
308}
309
310pub fn get_float64(key: &str) -> Option<f64> {
316 let configs = CONFIGS.lock().unwrap();
317 if let Some(value) = configs.get(key) {
318 match value {
319 Value::Number(n) => n.as_f64(),
320 _ => None,
321 }
322 } else {
323 None
324 }
325}
326
327pub fn get_float64_array(key: &str) -> Option<Vec<f64>> {
333 let configs = CONFIGS.lock().unwrap();
334 if let Some(value) = configs.get(key) {
335 if let Value::Array(arr) = value {
336 let mut float64_array = Vec::new();
337 for element in arr {
338 if let Value::Number(n) = element {
339 if let Some(int_value) = n.as_f64() {
340 float64_array.push(int_value);
341 }
342 }
343 }
344 Some(float64_array)
345 } else {
346 None
347 }
348 } else {
349 None
350 }
351}
352
353pub fn get_float32(key: &str) -> Option<f32> {
359 let configs = CONFIGS.lock().unwrap();
360 if let Some(value) = configs.get(key) {
361 match value {
362 Value::Number(n) => n.as_f64().map(|n| n as f32),
363 _ => None,
364 }
365 } else {
366 None
367 }
368}
369
370pub fn get_bool(key: &str) -> Option<bool> {
376 let configs = CONFIGS.lock().unwrap();
377 if let Some(value) = configs.get(key) {
378 value.as_bool()
379 } else {
380 None
381 }
382}
383
384pub fn get(key: &str) -> Option<Value> {
390 CONFIGS.lock().unwrap().get(key).cloned()
391}
392
393pub fn get_array(key: &str) -> Option<Vec<Value>> {
399 let configs = CONFIGS.lock().unwrap();
400 if let Some(value) = configs.get(key) {
401 if let Value::Array(arr) = value {
402 let mut array = Vec::new();
403 for element in arr {
404 if let Value::Object(_) = element {
405 array.push(element.clone());
406 }
407 }
408 Some(array)
409 } else {
410 None
411 }
412 } else {
413 None
414 }
415}
416
417pub fn get_map(key: &str) -> Option<Map<String, Value>> {
423 let configs = CONFIGS.lock().unwrap();
424 if let Some(map) = configs.get(key) {
425 map.as_object().cloned()
426 } else {
427 None
428 }
429}
430
431#[cfg(test)]
432mod tests {
433 use std::env;
434 use std::io::Write;
435 use std::path::{PathBuf};
436 use super::*;
437
438 #[test]
439 fn it_works() {
440 let data = r#"
441 {
442 "testGetString": "YesMan",
443 "testGetInt64": 43,
444 "testGetStringArray": [
445 "+44 1234567",
446 "+44 2345678"
447 ]
448 }"#;
449 let path_buf = env::current_exe().expect("Failed to get executable path");
450 let path_str = path_buf
451 .ancestors()
452 .nth(4) .expect("Invalid number of ancestors")
454 .to_str()
455 .expect("Failed to convert path to string");
456 let mut path = PathBuf::from(path_str);
457 path.push("config.json");
458 let mut file = std::fs::File::create(path.clone()).expect("create failed");
459 file.write_all(data.as_bytes()).expect("write failed");
460 add_config_path(path_str);
461 set_config_name("config.json");
462 read_config();
463 if path.as_path().exists() {
464 std::fs::remove_file(path.as_path()).expect("failed to delete test file");
465 }
466 assert_eq!(Some("YesMan".to_string()), get_string("testGetString"));
467 assert_eq!(Some(43), get_int64("testGetInt64"));
468 assert_eq!(Some(vec!["+44 1234567".to_string(), "+44 2345678".to_string()]), get_string_array("testGetStringArray"));
469 }
470}