hirust_config/
lib.rs

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    // cargo test run -- --show-output
14    #[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        // 检查扩展名是否为".yaml"
34        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            // 解析YAML内容到BTreeMap中,自动保持顺序
52            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}