Skip to main content

aster/config/
config_command.rs

1//! 配置命令
2//!
3//! 提供 /config 命令用于展示和管理配置
4
5use super::agents_md_parser::AgentsMdParser;
6use super::config_manager::{ConfigManager, ConfigSource};
7use serde_json::Value;
8use std::collections::HashMap;
9
10/// 配置展示选项
11#[derive(Debug, Clone, Default)]
12pub struct ConfigDisplayOptions {
13    /// 是否显示敏感信息
14    pub show_secrets: bool,
15    /// 是否显示来源
16    pub show_sources: bool,
17    /// 是否显示备份
18    pub show_backups: bool,
19    /// 输出格式
20    pub format: ConfigFormat,
21}
22
23/// 输出格式
24#[derive(Debug, Clone, Copy, Default)]
25pub enum ConfigFormat {
26    #[default]
27    Json,
28    Yaml,
29    Table,
30}
31
32/// 配置命令处理器
33pub struct ConfigCommand<'a> {
34    config_manager: &'a ConfigManager,
35}
36
37impl<'a> ConfigCommand<'a> {
38    /// 创建新的配置命令处理器
39    pub fn new(config_manager: &'a ConfigManager) -> Self {
40        Self { config_manager }
41    }
42
43    /// 展示当前配置
44    pub fn display(&self, options: ConfigDisplayOptions) -> String {
45        let mut output = String::new();
46
47        // 标题
48        output.push_str(&"=".repeat(60));
49        output.push_str("\nAster Configuration\n");
50        output.push_str(&"=".repeat(60));
51        output.push_str("\n\n");
52
53        // 配置内容
54        let config = if options.show_secrets {
55            serde_json::to_string_pretty(&self.config_manager.get_all()).unwrap_or_default()
56        } else {
57            self.config_manager.export(true)
58        };
59
60        output.push_str("**当前配置:**\n");
61        output.push_str("```json\n");
62        output.push_str(&config);
63        output.push_str("\n```\n\n");
64
65        // 配置来源
66        if options.show_sources {
67            output.push_str(&self.display_sources());
68        }
69
70        // 备份信息
71        if options.show_backups {
72            output.push_str(&self.display_backups());
73        }
74
75        // AGENTS.md 信息
76        output.push_str(&self.display_agents_md());
77
78        output.push('\n');
79        output.push_str(&"=".repeat(60));
80        output.push('\n');
81
82        output
83    }
84
85    /// 展示配置来源
86    fn display_sources(&self) -> String {
87        let mut output = String::from("**配置来源:**\n\n");
88
89        let sources = self.config_manager.get_config_source_info();
90
91        output.push_str("| 优先级 | 来源 | 路径 | 状态 |\n");
92        output.push_str("|--------|------|------|------|\n");
93
94        for info in sources {
95            let status = if info.exists { "OK" } else { "未找到" };
96            let path = info
97                .path
98                .as_ref()
99                .map(|p| p.display().to_string())
100                .unwrap_or_else(|| "N/A".to_string());
101
102            output.push_str(&format!(
103                "| {} | {:?} | {} | {} |\n",
104                info.priority, info.source, path, status
105            ));
106        }
107
108        output.push_str("\n**配置项来源:**\n\n");
109        output.push_str("| 配置键 | 值 | 来源 |\n");
110        output.push_str("|--------|-----|------|\n");
111
112        let config = self.config_manager.get_all();
113        let sources = self.config_manager.get_all_config_sources();
114
115        let important_keys = [
116            "api_key",
117            "model",
118            "max_tokens",
119            "api_provider",
120            "theme",
121            "enable_telemetry",
122        ];
123
124        for key in important_keys {
125            if let Some(value) = config.get(key) {
126                let formatted_value = self.format_value(value);
127                let source = sources.get(key).copied().unwrap_or(ConfigSource::Default);
128                output.push_str(&format!(
129                    "| {} | {} | {:?} |\n",
130                    key, formatted_value, source
131                ));
132            }
133        }
134
135        output.push('\n');
136        output
137    }
138
139    /// 格式化配置值
140    fn format_value(&self, value: &Value) -> String {
141        match value {
142            Value::Null => "null".to_string(),
143            Value::Bool(b) => b.to_string(),
144            Value::Number(n) => n.to_string(),
145            Value::String(s) => {
146                if s.len() > 30 {
147                    format!("{}...", s.get(..27).unwrap_or(s))
148                } else {
149                    s.clone()
150                }
151            }
152            Value::Array(_) | Value::Object(_) => {
153                let json = serde_json::to_string(value).unwrap_or_default();
154                if json.len() > 30 {
155                    format!("{}...", json.get(..27).unwrap_or(&json))
156                } else {
157                    json
158                }
159            }
160        }
161    }
162
163    /// 展示备份信息
164    fn display_backups(&self) -> String {
165        let mut output = String::from("**可用备份:**\n\n");
166
167        let user_backups = self.config_manager.list_backups("user");
168        let project_backups = self.config_manager.list_backups("project");
169        let local_backups = self.config_manager.list_backups("local");
170
171        output.push_str(&format!("用户配置备份: {}\n", user_backups.len()));
172        if let Some(latest) = user_backups.first() {
173            output.push_str(&format!("  最新: {}\n", latest));
174        }
175
176        output.push_str(&format!("项目配置备份: {}\n", project_backups.len()));
177        if let Some(latest) = project_backups.first() {
178            output.push_str(&format!("  最新: {}\n", latest));
179        }
180
181        output.push_str(&format!("本地配置备份: {}\n", local_backups.len()));
182        if let Some(latest) = local_backups.first() {
183            output.push_str(&format!("  最新: {}\n", latest));
184        }
185
186        output.push('\n');
187        output
188    }
189
190    /// 展示 AGENTS.md 信息
191    fn display_agents_md(&self) -> String {
192        let parser = AgentsMdParser::default();
193        let info = parser.parse();
194
195        let mut output = String::from("**AGENTS.md 状态:**\n\n");
196
197        if info.exists {
198            let stats = parser.get_stats();
199            let validation = parser.validate();
200
201            output.push_str(&format!("路径: {}\n", info.path.display()));
202            output.push_str("状态: 已找到 ✓\n");
203
204            if let Some(modified) = info.last_modified {
205                if let Ok(duration) = modified.elapsed() {
206                    output.push_str(&format!("最后修改: {}秒前\n", duration.as_secs()));
207                }
208            }
209
210            if let Some(stats) = stats {
211                output.push_str(&format!(
212                    "大小: {} 字节 ({} 行, {} 字符)\n",
213                    stats.size, stats.lines, stats.chars
214                ));
215            }
216
217            if !validation.warnings.is_empty() {
218                output.push_str("\n警告:\n");
219                for warning in validation.warnings {
220                    output.push_str(&format!("  - {}\n", warning));
221                }
222            }
223        } else {
224            output.push_str(&format!("路径: {}\n", info.path.display()));
225            output.push_str("状态: 未找到 ✗\n");
226            output.push_str("\n提示: 创建 AGENTS.md 文件为 AI Agent 提供项目指导。\n");
227        }
228
229        output.push('\n');
230        output
231    }
232
233    /// 获取特定配置项
234    pub fn get(&self, key: &str) -> String {
235        match self.config_manager.get_with_source::<Value>(key) {
236            Some((value, source, path)) => {
237                let path_info = path.map(|p| format!(" ({:?})", p)).unwrap_or_default();
238                format!(
239                    "{} = {} (来源: {:?}{})",
240                    key,
241                    serde_json::to_string_pretty(&value).unwrap_or_default(),
242                    source,
243                    path_info
244                )
245            }
246            None => format!("{} = 未设置", key),
247        }
248    }
249
250    /// 设置配置项
251    pub fn set(&self, key: &str, value: Value, target: &str) -> String {
252        let mut config = HashMap::new();
253        config.insert(key.to_string(), value.clone());
254
255        let result = match target {
256            "local" => self.config_manager.save_local(&config),
257            "project" => self.config_manager.save_project(&config),
258            _ => self.config_manager.save(Some(&config)),
259        };
260
261        match result {
262            Ok(_) => format!("已设置 {} = {:?} 到 {} 配置", key, value, target),
263            Err(e) => format!("设置失败: {}", e),
264        }
265    }
266
267    /// 列出备份
268    pub fn list_backups(&self, config_type: &str) -> String {
269        let backups = self.config_manager.list_backups(config_type);
270
271        if backups.is_empty() {
272            return format!("未找到 {} 配置的备份", config_type);
273        }
274
275        let mut output = format!("{} 配置的备份:\n\n", config_type);
276        for (index, backup) in backups.iter().enumerate() {
277            output.push_str(&format!("{}. {}\n", index + 1, backup));
278        }
279        output
280    }
281
282    /// 恢复备份
283    pub fn restore(
284        &self,
285        backup_filename: &str,
286        config_type: &str,
287        manager: &mut ConfigManager,
288    ) -> String {
289        match manager.restore_from_backup(backup_filename, config_type) {
290            Ok(_) => format!("已从 {} 恢复 {} 配置", backup_filename, config_type),
291            Err(e) => format!("恢复失败: {}", e),
292        }
293    }
294
295    /// 重置配置
296    pub fn reset(&self, manager: &mut ConfigManager) -> String {
297        manager.reset();
298        "配置已重置为默认值".to_string()
299    }
300
301    /// 导出配置
302    pub fn export_config(&self, mask_secrets: bool) -> String {
303        self.config_manager.export(mask_secrets)
304    }
305
306    /// 导入配置
307    pub fn import_config(&self, config_json: &str, manager: &mut ConfigManager) -> String {
308        match manager.import(config_json) {
309            Ok(_) => "配置导入成功".to_string(),
310            Err(e) => format!("导入失败: {}", e),
311        }
312    }
313
314    /// 获取帮助信息
315    pub fn help(&self) -> String {
316        r#"
317Aster 配置命令
318
319用法:
320  /config                   - 显示当前配置
321  /config get <key>         - 获取特定配置值
322  /config set <key> <value> - 设置配置值
323  /config backups [type]    - 列出可用备份 (user/project/local)
324  /config restore <file>    - 从备份恢复配置
325  /config reset             - 重置为默认配置
326  /config export            - 导出配置(敏感信息已掩码)
327  /config import <json>     - 从 JSON 导入配置
328  /config help              - 显示此帮助信息
329
330示例:
331  /config get model
332  /config set theme dark
333  /config backups user
334  /config restore settings.2024-01-01T12-00-00.yaml
335
336配置来源(优先级从低到高):
337  0. default              - 内置默认值
338  1. userSettings         - 用户全局配置 (~/.aster/settings.yaml)
339  2. projectSettings      - 项目配置 (./.aster/settings.yaml)
340  3. localSettings        - 本地配置 (./.aster/settings.local.yaml)
341  4. envSettings          - 环境变量 (ASTER_*)
342  5. flagSettings         - 命令行标志 (--settings)
343  6. policySettings       - 企业策略 (~/.aster/managed_settings.yaml)
344"#
345        .to_string()
346    }
347}
348
349/// 创建配置命令实例
350pub fn create_config_command(config_manager: &ConfigManager) -> ConfigCommand<'_> {
351    ConfigCommand::new(config_manager)
352}
353
354#[cfg(test)]
355mod tests {
356    use super::*;
357
358    #[test]
359    fn test_config_command_help() {
360        let manager = ConfigManager::default();
361        let cmd = ConfigCommand::new(&manager);
362        let help = cmd.help();
363        assert!(help.contains("/config"));
364        assert!(help.contains("配置来源"));
365    }
366
367    #[test]
368    fn test_config_command_get() {
369        let manager = ConfigManager::default();
370        let cmd = ConfigCommand::new(&manager);
371        let result = cmd.get("model");
372        assert!(result.contains("model"));
373    }
374
375    #[test]
376    fn test_config_command_display() {
377        let manager = ConfigManager::default();
378        let cmd = ConfigCommand::new(&manager);
379        let output = cmd.display(ConfigDisplayOptions::default());
380        assert!(output.contains("Aster Configuration"));
381    }
382
383    #[test]
384    fn test_format_value() {
385        let manager = ConfigManager::default();
386        let cmd = ConfigCommand::new(&manager);
387
388        assert_eq!(cmd.format_value(&Value::Bool(true)), "true");
389        assert_eq!(cmd.format_value(&Value::Number(42.into())), "42");
390        assert_eq!(cmd.format_value(&Value::String("test".to_string())), "test");
391    }
392}