better_config_loader/
json.rs1use better_config_core::override_env::merge_with_env_uppercase;
2use better_config_core::{AbstractConfig, Error};
3use better_config_core::misc;
4use std::collections::{HashMap, HashSet};
5use std::fs;
6
7pub trait JsonConfig<T = HashMap<String, String>>: AbstractConfig<T> {
9 fn load(target: Option<String>) -> Result<T, Error>
17 where
18 T: Default,
19 HashMap<String, String>: Into<T>,
20 Self: Sized,
21 {
22 Self::load_with_override(target, &HashSet::new())
23 }
24
25 fn load_with_override(target: Option<String>, excluded_keys: &HashSet<String>) -> Result<T, Error>
34 where
35 T: Default,
36 HashMap<String, String>: Into<T>,
37 Self: Sized,
38 {
39 let target = target.or(Some("config.json".to_string()));
40
41 let mut json_map = HashMap::new();
42
43 if let Some(target) = target {
44 let file_paths = misc::validate_and_split_paths(&target)?;
45
46 for file_path in file_paths {
47 misc::check_file_accessibility(&file_path)?;
49
50 let content = fs::read_to_string(&file_path)
51 .map_err(|e| Error::IoError {
52 operation: format!("read file '{}'", file_path),
53 source: Some(Box::new(e)),
54 })?;
55
56 let value: serde_json::Value = serde_json::from_str(&content)
57 .map_err(|e| Error::parse_json_error(&file_path, e))?;
58
59 flatten_json_value(&value, None, &mut json_map)
60 .map_err(|e| Error::value_conversion_error("json", "string", &format!("{}", e)))?;
61 }
62 }
63
64 let json_map = merge_with_env_uppercase(json_map, None, excluded_keys);
66
67 Ok(json_map.into())
68 }
69}
70
71fn flatten_json_value(value: &serde_json::Value, parent_key: Option<String>, map: &mut HashMap<String, String>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
72 match value {
73 serde_json::Value::Object(obj) => {
74 for (key, val) in obj {
75 let new_key = match &parent_key {
76 Some(parent) => format!("{}.{}", parent, key),
77 None => key.to_string(),
78 };
79 flatten_json_value(val, Some(new_key), map)?;
80 }
81 }
82 serde_json::Value::Array(arr) => {
83 for (i, val) in arr.iter().enumerate() {
84 let new_key = match &parent_key {
85 Some(parent) => format!("{}[{}]", parent, i),
86 None => i.to_string(),
87 };
88 flatten_json_value(val, Some(new_key), map)?;
89 }
90 }
91 serde_json::Value::String(s) => {
92 if let Some(key) = parent_key {
93 map.insert(key, s.to_string());
94 }
95 }
96 serde_json::Value::Number(n) => {
97 if let Some(key) = parent_key {
98 let num_str = if n.is_i64() {
99 n.as_i64()
100 .map(|v| v.to_string())
101 .unwrap_or_else(|| n.to_string())
102 } else if n.is_f64() {
103 n.as_f64()
104 .map(|v| v.to_string())
105 .unwrap_or_else(|| n.to_string())
106 } else {
107 n.to_string()
108 };
109 map.insert(key, num_str);
110 }
111 }
112 serde_json::Value::Bool(b) => {
113 if let Some(key) = parent_key {
114 map.insert(key, b.to_string());
115 }
116 }
117 serde_json::Value::Null => {}
118 }
119
120 Ok(())
121}