Skip to main content

openlark_client/
utils.rs

1use crate::config::ConfigSummary;
2use crate::{configuration_error, validation_error, with_context, Config, Result};
3use openlark_core::error::ErrorTrait;
4use std::env;
5
6/// 🔍 检查环境变量配置
7///
8/// 验证飞书应用所需的环境变量是否正确设置
9///
10/// # 返回
11/// - `Ok(())`: 环境变量配置正确
12/// - `Err(Error)`: 环境变量配置错误,包含详细的错误信息和恢复建议
13///
14/// # 示例
15/// ```rust,no_run
16/// use openlark_client::{prelude::*, utils};
17///
18/// match utils::check_env_config() {
19///     Ok(()) => println!("环境变量配置正确"),
20///     Err(error) => {
21///         eprintln!("❌ {}", error.user_message().unwrap_or("未知错误"));
22///         for step in error.recovery_steps() {
23///             eprintln!("• {}", step);
24///         }
25///     }
26/// }
27/// ```
28pub fn check_env_config() -> Result<()> {
29    // 检查 OPENLARK_APP_ID
30    let app_id = env::var("OPENLARK_APP_ID")
31        .map_err(|_| configuration_error("环境变量检查失败 [variable: OPENLARK_APP_ID]"))?;
32
33    if app_id.is_empty() {
34        return with_context(
35            Err(validation_error(
36                "OPENLARK_APP_ID",
37                "应用ID环境变量不能为空",
38            )),
39            "validation",
40            "env_config",
41        );
42    }
43
44    // 检查 OPENLARK_APP_SECRET
45    let app_secret = env::var("OPENLARK_APP_SECRET")
46        .map_err(|_| configuration_error("环境变量检查失败 [variable: OPENLARK_APP_SECRET]"))?;
47
48    if app_secret.is_empty() {
49        return with_context(
50            Err(validation_error(
51                "OPENLARK_APP_SECRET",
52                "应用密钥环境变量不能为空",
53            )),
54            "validation",
55            "env_config",
56        );
57    }
58
59    // 检查可选的环境变量
60    if let Ok(base_url) = env::var("OPENLARK_BASE_URL") {
61        if !base_url.starts_with("http://") && !base_url.starts_with("https://") {
62            return with_context(
63                Err(validation_error(
64                    "OPENLARK_BASE_URL",
65                    "基础URL必须以http://或https://开头",
66                )),
67                "validation",
68                "env_config",
69            );
70        }
71    }
72
73    // 检查超时设置
74    if let Ok(timeout_str) = env::var("OPENLARK_TIMEOUT") {
75        if timeout_str.parse::<u64>().is_err() {
76            return with_context(
77                Err(validation_error(
78                    "OPENLARK_TIMEOUT",
79                    "超时设置必须是有效的数字(秒数)",
80                )),
81                "validation",
82                "env_config",
83            );
84        }
85    }
86
87    Ok(())
88}
89
90/// 🔧 从环境变量创建配置
91///
92/// 自动读取环境变量并创建客户端配置
93///
94/// # 返回
95/// - `Ok(Config)`: 成功创建配置
96/// - `Err(Error)`: 配置创建失败,包含详细错误信息
97pub fn create_config_from_env() -> Result<Config> {
98    // 先检查环境变量
99    check_env_config()?;
100
101    let app_id = env::var("OPENLARK_APP_ID").unwrap();
102    let app_secret = env::var("OPENLARK_APP_SECRET").unwrap();
103
104    let base_url =
105        env::var("OPENLARK_BASE_URL").unwrap_or_else(|_| "https://open.feishu.cn".to_string());
106
107    let timeout = env::var("OPENLARK_TIMEOUT")
108        .ok()
109        .and_then(|t| t.parse().ok())
110        .map(std::time::Duration::from_secs);
111
112    let enable_log = env::var("OPENLARK_ENABLE_LOG")
113        .ok()
114        .and_then(|l| l.parse().ok())
115        .unwrap_or(false);
116
117    let mut config = Config::builder()
118        .app_id(app_id)
119        .app_secret(app_secret)
120        .base_url(base_url)
121        .enable_log(enable_log);
122
123    if let Some(timeout_duration) = timeout {
124        config = config.timeout(timeout_duration);
125    }
126
127    with_context(config.build(), "operation", "create_config_from_env")
128}
129
130/// 📊 获取配置摘要
131///
132/// 返回当前配置的摘要信息,用于调试和监控
133pub fn get_config_summary(config: &Config) -> ConfigSummary {
134    config.summary()
135}
136
137/// 🏷️ 获取启用的功能列表
138///
139/// 返回当前编译时启用的功能标志列表
140pub fn get_enabled_features() -> Vec<&'static str> {
141    // 基础功能(始终启用)
142    #[allow(unused_mut)]
143    let mut features = vec!["auth"];
144
145    // 可选功能(基于编译时标志)
146    #[cfg(feature = "communication")]
147    features.push("communication");
148
149    #[cfg(feature = "docs")]
150    features.push("docs");
151
152    #[cfg(feature = "security")]
153    features.push("security");
154
155    #[cfg(feature = "hr")]
156    features.push("hr");
157
158    #[cfg(feature = "ai")]
159    features.push("ai");
160
161    features
162}
163
164/// 🔍 验证功能依赖关系
165///
166/// 检查启用的功能是否满足依赖关系要求
167pub fn validate_feature_dependencies() -> Result<Vec<String>> {
168    let enabled_features = get_enabled_features();
169    let mut issues = Vec::new();
170
171    // 检查核心依赖
172    if enabled_features.contains(&"communication") && !enabled_features.contains(&"auth") {
173        issues.push("通讯服务 (communication) 需要启用认证服务 (auth)".to_string());
174    }
175
176    if enabled_features.contains(&"docs") && !enabled_features.contains(&"auth") {
177        issues.push("文档服务 (docs) 需要启用认证服务 (auth)".to_string());
178    }
179
180    if enabled_features.contains(&"hr") && !enabled_features.contains(&"auth") {
181        issues.push("人力资源服务 (hr) 需要启用认证服务 (auth)".to_string());
182    }
183
184    if enabled_features.contains(&"ai") && !enabled_features.contains(&"auth") {
185        issues.push("AI服务 (ai) 需要启用认证服务 (auth)".to_string());
186    }
187
188    if enabled_features.contains(&"calendar") && !enabled_features.contains(&"auth") {
189        issues.push("日历服务 (calendar) 需要启用认证服务 (auth)".to_string());
190    }
191
192    if enabled_features.contains(&"admin") && !enabled_features.contains(&"hr") {
193        issues.push("管理服务 (admin) 建议启用人力资源服务 (hr) 以获得完整功能".to_string());
194    }
195
196    if enabled_features.contains(&"approval") && !enabled_features.contains(&"auth") {
197        issues.push("审批服务 (approval) 需要启用认证服务 (auth)".to_string());
198    }
199
200    if issues.is_empty() {
201        Ok(issues)
202    } else {
203        with_context(
204            Err(configuration_error(format!(
205                "发现 {} 个功能依赖问题: {}",
206                issues.len(),
207                issues.join("; ")
208            ))),
209            "validation",
210            "feature_dependencies",
211        )
212    }
213}
214
215/// 🏥 诊断系统配置
216///
217/// 执行全面的系统配置检查,包括环境变量、功能依赖等
218pub fn diagnose_system() -> SystemDiagnostics {
219    let mut diagnostics = SystemDiagnostics::new();
220
221    // 检查环境变量
222    match check_env_config() {
223        Ok(()) => {
224            diagnostics.env_config_status = "✅ 正常".to_string();
225        }
226        Err(error) => {
227            diagnostics.env_config_status =
228                format!("❌ {}", error.user_message().unwrap_or("未知错误"));
229            diagnostics.add_issue("环境变量", error.user_message().unwrap_or("未知错误"));
230        }
231    }
232
233    // 检查功能依赖
234    match validate_feature_dependencies() {
235        Ok(_) => {
236            diagnostics.feature_deps_status = "✅ 正常".to_string();
237        }
238        Err(error) => {
239            diagnostics.feature_deps_status =
240                format!("❌ {}", error.user_message().unwrap_or("未知错误"));
241            diagnostics.add_issue("功能依赖", error.user_message().unwrap_or("未知错误"));
242        }
243    }
244
245    // 列出启用的功能
246    diagnostics.enabled_features = get_enabled_features()
247        .into_iter()
248        .map(|s| s.to_string())
249        .collect();
250
251    diagnostics
252}
253
254/// 🏥 系统诊断结果
255#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
256pub struct SystemDiagnostics {
257    /// 🌍 环境变量配置状态
258    pub env_config_status: String,
259    /// 🔗 功能依赖状态
260    pub feature_deps_status: String,
261    /// 🏷️ 启用的功能列表
262    pub enabled_features: Vec<String>,
263    /// ⚠️ 发现的问题列表
264    pub issues: Vec<DiagnosticIssue>,
265}
266
267impl SystemDiagnostics {
268    /// 创建新的诊断结果
269    pub fn new() -> Self {
270        Self {
271            env_config_status: "未检查".to_string(),
272            feature_deps_status: "未检查".to_string(),
273            enabled_features: Vec::new(),
274            issues: Vec::new(),
275        }
276    }
277
278    /// 添加问题到诊断结果
279    pub fn add_issue(&mut self, category: &str, description: &str) {
280        self.issues.push(DiagnosticIssue {
281            category: category.to_string(),
282            description: description.to_string(),
283        });
284    }
285
286    /// 获取健康状态摘要
287    pub fn health_summary(&self) -> String {
288        let healthy_count = self.issues.len();
289        if healthy_count == 0 {
290            "🟢 系统配置健康".to_string()
291        } else {
292            format!("🟡 发现 {} 个配置问题", healthy_count)
293        }
294    }
295
296    /// 检查是否有严重问题
297    pub fn has_critical_issues(&self) -> bool {
298        self.issues
299            .iter()
300            .any(|issue| issue.category.contains("环境变量") || issue.category.contains("功能依赖"))
301    }
302}
303
304impl Default for SystemDiagnostics {
305    fn default() -> Self {
306        Self::new()
307    }
308}
309
310/// 🔍 诊断问题条目
311#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
312pub struct DiagnosticIssue {
313    /// 🏷️ 问题类别
314    pub category: String,
315    /// 📝 问题描述
316    pub description: String,
317}
318
319#[cfg(test)]
320#[allow(unused_imports)]
321mod tests {
322
323    #[test]
324    fn test_serialization_roundtrip() {
325        // 基础序列化测试
326        let json = r#"{"test": "value"}"#;
327        assert!(serde_json::from_str::<serde_json::Value>(json).is_ok());
328    }
329
330    #[test]
331    fn test_deserialization_from_json() {
332        // 基础反序列化测试
333        let json = r#"{"field": "data"}"#;
334        let value: serde_json::Value = serde_json::from_str(json).unwrap();
335        assert_eq!(value["field"], "data");
336    }
337}