1use std::fs;
7use std::path::{Path, PathBuf};
8
9use serde::de::DeserializeOwned;
10use toml::Value::Table;
11
12use crate::component::Component;
13use crate::scope::Scope;
14
15pub struct AppAllConfig {
17 pub toml_value: toml::Value,
18}
19
20impl AppAllConfig {
21 pub fn new<P: Into<PathBuf>>(config_path: Option<P>) -> Self {
23 let final_config_path = if let Some(path) = config_path {
24 path.into()
25 } else {
26 let exe_path = std::env::current_exe().unwrap_or_else(|e| {
28 panic!(
29 "[di] 无法获取可执行文件路径: {}。\n\
30 请检查程序运行环境,或手动传入配置路径。",
31 e
32 )
33 });
34
35 let config_dir = exe_path
36 .parent()
37 .unwrap_or_else(|| {
38 panic!(
39 "[di] 无法获取可执行文件父目录: {:?}。\n\
40 请手动传入配置路径。",
41 exe_path
42 )
43 })
44 .join("config");
45
46 config_dir.join("config.toml")
47 };
48
49 crate::lifecycle::set_sys_config(
50 crate::lifecycle::CONFIG_PATH,
51 final_config_path.to_str().unwrap().to_string(),
52 );
53 let toml_value = Self::load_config(final_config_path.as_path());
54 AppAllConfig { toml_value }
55 }
56
57 fn load_config(path: &Path) -> toml::Value {
62 if !path.exists() {
63 eprintln!("[di] 配置文件不存在: {:?},将使用默认配置", path);
64 return Table(toml::map::Map::new());
65 }
66
67 let content = fs::read_to_string(path).unwrap_or_else(|e| {
68 panic!(
69 "[di] 配置文件读取失败: {:?}\n\
70 错误: {}\n\
71 请检查文件权限和路径是否正确。",
72 path, e
73 )
74 });
75
76 let config: toml::Value = toml::from_str(&content).unwrap_or_else(|e| {
77 panic!(
78 "[di] 配置文件解析失败: {:?}\n\
79 错误: {}\n\
80 请检查 TOML 语法是否正确。",
81 path, e
82 )
83 });
84 eprintln!("[di] 配置文件加载成功: {:?}", path);
85 config
86 }
87
88 pub fn get<T: DeserializeOwned>(&self, key: &str) -> Option<T> {
90 let value = self.get_value(key)?;
91 T::deserialize(value.clone()).ok()
92 }
93
94 pub fn get_or_default<T: DeserializeOwned>(&self, key: &str, default: T) -> T {
96 self.get(key).unwrap_or(default)
97 }
98
99 pub fn get_value(&self, key: &str) -> Option<&toml::Value> {
101 let keys: Vec<&str> = key.split('.').collect();
102 let mut current = &self.toml_value;
103 for k in keys {
104 current = current.get(k)?;
105 }
106 Some(current)
107 }
108}
109
110impl Component for AppAllConfig {
113 type Deps = ();
114
115 fn build(_deps: Self::Deps) -> Self {
116 panic!("[di] AppAllConfig 只在 BuildContext::new() 内部构建,不应通过 Component::build() 调用")
117 }
118
119 const SCOPE: Scope = Scope::Singleton;
120}