1use serde::Deserialize;
2use std::collections::BTreeMap;
3use std::fs;
4use std::sync::{LazyLock, Mutex};
5use walkdir::WalkDir;
6
7static CONFIG_COLLECT: LazyLock<Mutex<BTreeMap<String, BTreeMap<String, serde_yml::Value>>>> = LazyLock::new(|| Mutex::new(BTreeMap::new()));
8
9#[cfg(test)]
10mod tests {
11 use super::*;
12
13 #[test]
15 fn run() {
16 load_config("./env");
17 print(|key, value| {
18 println!("{:?}: {:?}", key, value);
19 });
20 let version = get::<String>("env.config.app.SSL.OUT");
21 println!("{} {:?}", line!(), version);
22 }
23}
24
25#[allow(unused)]
26pub fn load_config(config_dir: &str) {
27 for entry in WalkDir::new(config_dir) {
28 let entry = entry.unwrap();
29 let file_path = entry.path();
30 let extension = file_path.extension().and_then(|s| s.to_str());
31
32 let file_name = file_path.file_name().and_then(|s| s.to_str()).unwrap();
33 if file_path.is_file() && extension == Some("yaml") {
35 let mut keys: Vec<String> = vec![];
36 for p in file_path.iter() {
37 if p.ne(".") {
38 if p.ne(file_name) {
39 keys.push(format!("{:?}", p).replace("\"", ""));
40 } else {
41 keys.push(format!("{:?}", p).replace(".yaml", "").replace("\"", ""));
42 }
43 }
44 }
45
46 let file_name = file_path.file_name().and_then(|s| s.to_str()).unwrap();
47 let file_name = file_name.replace(".yaml", "");
48 let yaml_content =
49 fs::read_to_string(file_path).expect(format!("读取{:?}失败", file_path).as_str());
50
51 let deserialized_map: BTreeMap<String, serde_yml::Value> = serde_yml::from_str(&yaml_content).unwrap();
53
54 CONFIG_COLLECT
55 .lock()
56 .unwrap()
57 .insert(String::from(keys.join(".")), deserialized_map.clone());
58 }
59 }
60}
61
62type PrintFn = fn(key: &String, value: &BTreeMap<String, serde_yml::Value>);
63
64#[allow(unused)]
65pub fn print(f: PrintFn) {
66 for (k, v) in CONFIG_COLLECT.lock().unwrap().iter() {
67 f(k, v);
68 }
69}
70
71#[allow(unused)]
72pub fn get<'a, T: Deserialize<'a>>(key: &str) -> Option<T> {
73 let mut result = None;
74 if key.contains(".") {
75 let keys = key.split(".").collect::<Vec<&str>>();
76 let mut config_collect: Option<BTreeMap<String, serde_yml::Value>> = None;
77 let mut value: Option<serde_yml::Value> = None;
78 let mut mapping: Option<serde_yml::Mapping> = None;
79
80 let mut temp_keys: Vec<String> = vec![];
81
82 for key in keys {
83 result = None;
84
85 temp_keys.push(key.to_string());
86
87 if config_collect.is_none() {
88 if let Some(bm) = CONFIG_COLLECT.lock().unwrap().get(&temp_keys.join(".").to_string()){
89 config_collect = Some(bm.clone());
90 }
91 } else {
92 if let Some(m) = &mapping {
93 value = m.get(&key.to_string()).cloned();
94 if value.is_some() {
95 if value.clone().unwrap().is_mapping() {
96 mapping = value.clone().unwrap().as_mapping().cloned();
97 value = None;
98 } else {
99 result = value.clone().and_then(|v| T::deserialize(v).ok());
100 }
101 } else {
102 result = value.clone().and_then(|v| T::deserialize(v).ok());
103 value = None;
104 }
105 } else if let Some(vv) = value.clone() {
106 if vv.is_mapping() {
107 match vv.as_mapping() {
108 Some(m) => {
109 mapping = Some(m.clone());
110 value = None;
111 }
112 None => (),
113 }
114 } else {
115 result = value.clone().and_then(|v| T::deserialize(v).ok());
116 value = None;
117 }
118 } else {
119 value = config_collect.clone().unwrap().get(&key.to_string()).cloned();
120 if let Some(vv) = value.clone() {
121 if vv.is_mapping() {
122 match vv.as_mapping() {
123 Some(m) => {
124 mapping = Some(m.clone());
125 value = None;
126 }
127 None => (),
128 }
129 } else {
130 result = value.clone().and_then(|v| T::deserialize(v).ok());
131 value = None;
132 }
133 }
134 }
135 }
136 }
137 }
138 result
139}