better_config_loader/
json.rs

1use better_config_core::{AbstractConfig, Error};
2use std::collections::HashMap;
3use std::fs;
4
5/// Indicates that structure can be initialized from JSON file.
6pub trait JsonConfig<T = HashMap<String, String>>: AbstractConfig<T> {
7    /// Load specified JSON file and initialize the structure.
8    ///
9    /// # Arguments
10    /// * `target` - Path to the JSON file.
11    ///
12    /// # Errors
13    /// * `Error::LoadFileError` - If the specified JSON file cannot be loaded or parsed.
14    fn load(target: Option<String>) -> Result<T, Error>
15    where
16        T: Default,
17        HashMap<String, String>: Into<T>,
18        Self: Sized,
19    {
20        let target = target.or(Some("config.json".to_string()));
21
22        let mut json_map = HashMap::new();
23
24        if let Some(target) = target {
25            let file_paths: Vec<String> = target
26                .split(',')
27                .map(|s| s.trim().to_string())
28                .filter(|s| !s.is_empty())
29                .collect();
30
31            for file_path in file_paths {
32                let content = fs::read_to_string(&file_path).map_err(|e| Error::LoadFileError {
33                    name: file_path.clone(),
34                    source: Some(Box::new(e)),
35                })?;
36
37                let value: serde_json::Value = serde_json::from_str(&content).map_err(|e| Error::LoadFileError {
38                    name: file_path.clone(),
39                    source: Some(Box::new(e)),
40                })?;
41
42                flatten_json_value(&value, None, &mut json_map);
43            }
44        }
45
46        Ok(json_map.into())
47    }
48}
49
50fn flatten_json_value(value: &serde_json::Value, parent_key: Option<String>, map: &mut HashMap<String, String>) {
51    match value {
52        serde_json::Value::Object(obj) => {
53            for (key, val) in obj {
54                let new_key = match &parent_key {
55                    Some(parent) => format!("{}.{}", parent, key),
56                    None => key.to_string(),
57                };
58                flatten_json_value(val, Some(new_key), map);
59            }
60        }
61        serde_json::Value::Array(arr) => {
62            for (i, val) in arr.iter().enumerate() {
63                let new_key = match &parent_key {
64                    Some(parent) => format!("{}[{}]", parent, i),
65                    None => i.to_string(),
66                };
67                flatten_json_value(val, Some(new_key), map);
68            }
69        }
70        serde_json::Value::String(s) => {
71            if let Some(key) = parent_key {
72                map.insert(key, s.to_string());
73            }
74        }
75        serde_json::Value::Number(n) => {
76            if let Some(key) = parent_key {
77                if n.is_i64() {
78                    map.insert(key, n.as_i64().unwrap().to_string());
79                } else if n.is_f64() {
80                    map.insert(key, n.as_f64().unwrap().to_string());
81                } else {
82                    map.insert(key, n.to_string());
83                }
84            }
85        }
86        serde_json::Value::Bool(b) => {
87            if let Some(key) = parent_key {
88                map.insert(key, b.to_string());
89            }
90        }
91        serde_json::Value::Null => {}
92    }
93}