actr_cli/commands/
discovery.rs

1//! Discovery 命令实现
2//!
3//! 展示多重复用模式:服务发现 → 验证 → 可选安装
4
5use anyhow::Result;
6use async_trait::async_trait;
7
8use crate::core::{
9    ActrCliError, Command, CommandContext, CommandResult, ComponentType, DependencySpec,
10    ServiceInfo,
11};
12
13/// Discovery 命令
14pub struct DiscoveryCommand {
15    filter: Option<String>,
16    #[allow(dead_code)]
17    verbose: bool,
18    auto_install: bool,
19}
20
21#[async_trait]
22impl Command for DiscoveryCommand {
23    async fn execute(&self, context: &CommandContext) -> Result<CommandResult> {
24        // 获取复用组件
25        let (service_discovery, user_interface, _config_manager) = {
26            let container = context.container.lock().unwrap();
27            (
28                container.get_service_discovery()?,
29                container.get_user_interface()?,
30                container.get_config_manager()?,
31            )
32        };
33
34        // 🔍 阶段1: 服务发现
35        println!("🔍 正在扫描网络中的 Actor 服务...");
36
37        let filter = self.create_service_filter();
38        let services = service_discovery.discover_services(filter.as_ref()).await?;
39
40        if services.is_empty() {
41            println!("ℹ️ 当前网络中没有发现可用的 Actor 服务");
42            return Ok(CommandResult::Success("No services discovered".to_string()));
43        }
44
45        // 显示发现的服务
46        self.display_services_table(&services);
47
48        // 🎯 阶段2: 用户交互选择
49        let selected_index = user_interface
50            .select_service_from_list(&services, |s| format!("{} ({})", s.name, s.version))
51            .await?;
52
53        let selected_service = &services[selected_index];
54
55        // 显示服务详情和操作菜单
56        self.display_service_details(selected_service).await?;
57
58        // 询问用户操作
59        let action_menu = vec![
60            "查看服务详情".to_string(),
61            "导出 proto 文件".to_string(),
62            "添加到配置文件".to_string(),
63        ];
64
65        let action_choice = user_interface
66            .select_string_from_list(&action_menu, |s| s.clone())
67            .await?;
68
69        match action_choice {
70            0 => {
71                // 查看详情
72                self.show_detailed_service_info(selected_service, &service_discovery)
73                    .await?;
74                Ok(CommandResult::Success(
75                    "Service details displayed".to_string(),
76                ))
77            }
78            1 => {
79                // 导出 proto 文件
80                self.export_proto_files(selected_service, &service_discovery)
81                    .await?;
82                Ok(CommandResult::Success("Proto files exported".to_string()))
83            }
84            2 => {
85                // 添加到配置文件 - 复用架构的核心流程
86                self.add_to_config_with_validation(selected_service, context)
87                    .await
88            }
89            _ => Ok(CommandResult::Success("Invalid choice".to_string())),
90        }
91    }
92
93    fn required_components(&self) -> Vec<ComponentType> {
94        // Discovery 命令需要的组件(支持完整的复用流程)
95        vec![
96            ComponentType::ServiceDiscovery,     // 核心服务发现
97            ComponentType::UserInterface,        // 交互界面
98            ComponentType::ConfigManager,        // 配置管理
99            ComponentType::DependencyResolver,   // 依赖解析(验证阶段)
100            ComponentType::NetworkValidator,     // 网络验证(验证阶段)
101            ComponentType::FingerprintValidator, // 指纹验证(验证阶段)
102            ComponentType::CacheManager,         // 缓存管理(安装阶段)
103            ComponentType::ProtoProcessor,       // Proto处理(安装阶段)
104        ]
105    }
106
107    fn name(&self) -> &str {
108        "discovery"
109    }
110
111    fn description(&self) -> &str {
112        "发现网络中可用的 Actor 服务 (复用架构 + check-first)"
113    }
114}
115
116impl DiscoveryCommand {
117    pub fn new(filter: Option<String>, verbose: bool, auto_install: bool) -> Self {
118        Self {
119            filter,
120            verbose,
121            auto_install,
122        }
123    }
124
125    /// 创建服务过滤器
126    fn create_service_filter(&self) -> Option<crate::core::ServiceFilter> {
127        self.filter
128            .as_ref()
129            .map(|pattern| crate::core::ServiceFilter {
130                name_pattern: Some(pattern.clone()),
131                version_range: None,
132                tags: None,
133            })
134    }
135
136    /// 显示服务列表表格
137    fn display_services_table(&self, services: &[ServiceInfo]) {
138        println!();
139        println!("🔍 发现的 Actor 服务:");
140        println!();
141        println!("┌─────────────────┬─────────┬─────────────────────────────────┐");
142        println!("│ 服务名称        │ 版本    │ 简介                            │");
143        println!("├─────────────────┼─────────┼─────────────────────────────────┤");
144
145        for service in services {
146            let description = service
147                .description
148                .as_deref()
149                .unwrap_or("无描述")
150                .chars()
151                .take(28)
152                .collect::<String>();
153
154            println!(
155                "│ {:15} │ {:7} │ {:31} │",
156                service.name.chars().take(15).collect::<String>(),
157                service.version.chars().take(7).collect::<String>(),
158                description
159            );
160        }
161
162        println!("└─────────────────┴─────────┴─────────────────────────────────┘");
163        println!();
164        println!("→ 使用 ↑↓ 选择服务,回车查看选项,q 退出");
165        println!();
166    }
167
168    /// 显示服务详情
169    async fn display_service_details(&self, service: &ServiceInfo) -> Result<()> {
170        println!("📋 选择的服务: {} ({})", service.name, service.version);
171        if let Some(desc) = &service.description {
172            println!("📝 描述: {desc}");
173        }
174        println!("🔗 URI: {}", service.uri);
175        println!("🔐 指纹: {}", service.fingerprint);
176        println!("📊 方法数量: {}", service.methods.len());
177        println!();
178        Ok(())
179    }
180
181    /// 显示详细服务信息
182    async fn show_detailed_service_info(
183        &self,
184        service: &ServiceInfo,
185        service_discovery: &std::sync::Arc<dyn crate::core::ServiceDiscovery>,
186    ) -> Result<()> {
187        println!("📖 {} 详细信息:", service.name);
188        println!("════════════════════════════════════════");
189
190        let details = service_discovery.get_service_details(&service.uri).await?;
191
192        println!("🏷️ 服务名称: {}", details.info.name);
193        println!("📦 版本: {}", details.info.version);
194        println!("🔗 URI: {}", details.info.uri);
195        println!("🔐 指纹: {}", details.info.fingerprint);
196
197        if let Some(desc) = &details.info.description {
198            println!("📝 描述: {desc}");
199        }
200
201        println!();
202        println!("📋 可用方法:");
203        for method in &details.info.methods {
204            println!(
205                "  • {}: {} → {}",
206                method.name, method.input_type, method.output_type
207            );
208        }
209
210        if !details.dependencies.is_empty() {
211            println!();
212            println!("🔗 依赖服务:");
213            for dep in &details.dependencies {
214                println!("  • {dep}");
215            }
216        }
217
218        println!();
219        println!("📁 Proto 文件:");
220        for proto in &details.proto_files {
221            println!("  • {} ({} 个服务)", proto.name, proto.services.len());
222        }
223
224        Ok(())
225    }
226
227    /// 导出 proto 文件
228    async fn export_proto_files(
229        &self,
230        service: &ServiceInfo,
231        service_discovery: &std::sync::Arc<dyn crate::core::ServiceDiscovery>,
232    ) -> Result<()> {
233        println!("📤 正在导出 {} 的 proto 文件...", service.name);
234
235        let proto_files = service_discovery.get_service_proto(&service.uri).await?;
236
237        for proto in &proto_files {
238            let file_path = format!("./exported_{}", proto.name);
239            std::fs::write(&file_path, &proto.content)?;
240            println!("✅ 已导出: {file_path}");
241        }
242
243        println!("🎉 导出完成,共 {} 个文件", proto_files.len());
244        Ok(())
245    }
246
247    /// 添加到配置文件 - 复用架构的核心流程
248    async fn add_to_config_with_validation(
249        &self,
250        service: &ServiceInfo,
251        context: &CommandContext,
252    ) -> Result<CommandResult> {
253        let (config_manager, user_interface) = {
254            let container = context.container.lock().unwrap();
255            (
256                container.get_config_manager()?,
257                container.get_user_interface()?,
258            )
259        };
260
261        // 转换为依赖规范
262        let dependency_spec = DependencySpec {
263            name: service.name.clone(),
264            uri: service.uri.clone(),
265            version: Some(service.version.clone()),
266            fingerprint: Some(service.fingerprint.clone()),
267        };
268
269        println!("📝 正在添加 {} 到配置文件...", service.name);
270
271        // 备份配置
272        let backup = config_manager.backup_config().await?;
273
274        // 更新配置
275        match config_manager.update_dependency(&dependency_spec).await {
276            Ok(_) => {
277                println!("✅ 已添加 {} 到配置文件", service.name);
278            }
279            Err(e) => {
280                config_manager.restore_backup(backup).await?;
281                return Err(ActrCliError::Config {
282                    message: format!("配置更新失败: {e}"),
283                }
284                .into());
285            }
286        }
287
288        // 🔍 复用 check 流程验证新依赖
289        println!();
290        println!("🔍 正在验证新依赖...");
291
292        let validation_pipeline = {
293            let mut container = context.container.lock().unwrap();
294            container.get_validation_pipeline()?
295        };
296
297        match validation_pipeline
298            .validate_dependencies(&[dependency_spec.clone()])
299            .await
300        {
301            Ok(validation_results) => {
302                let all_passed = validation_results.iter().all(|v| v.is_available);
303
304                if !all_passed {
305                    // 验证失败,回滚配置
306                    println!("❌ 依赖验证失败,正在回滚配置修改...");
307                    config_manager.restore_backup(backup).await?;
308
309                    // 显示验证失败的详细信息
310                    for validation in &validation_results {
311                        if !validation.is_available {
312                            println!(
313                                "  • {}: {}",
314                                validation.dependency,
315                                validation.error.as_deref().unwrap_or("验证失败")
316                            );
317                        }
318                    }
319
320                    return Err(ActrCliError::ValidationFailed {
321                        details: "依赖验证失败".to_string(),
322                    }
323                    .into());
324                } else {
325                    // 验证成功
326                    println!("  ├─ 📋 服务存在性检查 ✅");
327                    println!("  ├─ 🌐 网络连通性测试 ✅");
328                    println!("  └─ 🔐 指纹完整性验证 ✅");
329
330                    // 清理备份
331                    config_manager.remove_backup(backup).await?;
332                }
333            }
334            Err(e) => {
335                // 验证出错,回滚配置
336                println!("❌ 验证过程出错,正在回滚配置修改...");
337                config_manager.restore_backup(backup).await?;
338                return Err(e);
339            }
340        }
341
342        // 🤔 询问是否立即安装
343        println!();
344        let should_install = if self.auto_install {
345            true
346        } else {
347            user_interface.confirm("🤔 是否立即安装此依赖?").await?
348        };
349
350        if should_install {
351            // 📦 复用 install 流程
352            println!();
353            println!("📦 正在安装 {}...", service.name);
354
355            let install_pipeline = {
356                let mut container = context.container.lock().unwrap();
357                container.get_install_pipeline()?
358            };
359
360            match install_pipeline
361                .install_dependencies(&[dependency_spec])
362                .await
363            {
364                Ok(install_result) => {
365                    println!("  ├─ 📦 缓存 proto 文件 ✅");
366                    println!("  ├─ 🔒 更新锁文件 ✅");
367                    println!("  └─ ✅ 安装完成");
368                    println!();
369                    println!("💡 建议: 运行 'actr gen' 生成最新代码");
370
371                    Ok(CommandResult::Install(install_result))
372                }
373                Err(e) => {
374                    eprintln!("❌ 安装失败: {e}");
375                    Ok(CommandResult::Success(
376                        "Dependency added but installation failed".to_string(),
377                    ))
378                }
379            }
380        } else {
381            println!("✅ 依赖已添加到配置文件");
382            println!("💡 运行 'actr install' 来安装依赖");
383            Ok(CommandResult::Success(
384                "Dependency added to configuration".to_string(),
385            ))
386        }
387    }
388}
389
390impl Default for DiscoveryCommand {
391    fn default() -> Self {
392        Self::new(None, false, false)
393    }
394}
395
396#[cfg(test)]
397mod tests {
398    use super::*;
399
400    #[test]
401    fn test_create_service_filter() {
402        let cmd = DiscoveryCommand::new(Some("user-*".to_string()), false, false);
403        let filter = cmd.create_service_filter();
404
405        assert!(filter.is_some());
406        let filter = filter.unwrap();
407        assert_eq!(filter.name_pattern, Some("user-*".to_string()));
408    }
409
410    #[test]
411    fn test_create_service_filter_none() {
412        let cmd = DiscoveryCommand::new(None, false, false);
413        let filter = cmd.create_service_filter();
414
415        assert!(filter.is_none());
416    }
417
418    #[test]
419    fn test_required_components() {
420        let cmd = DiscoveryCommand::default();
421        let components = cmd.required_components();
422
423        // Discovery 命令需要支持完整的复用流程
424        assert!(components.contains(&ComponentType::ServiceDiscovery));
425        assert!(components.contains(&ComponentType::UserInterface));
426        assert!(components.contains(&ComponentType::ConfigManager));
427        assert!(components.contains(&ComponentType::DependencyResolver));
428        assert!(components.contains(&ComponentType::NetworkValidator));
429        assert!(components.contains(&ComponentType::FingerprintValidator));
430        assert!(components.contains(&ComponentType::CacheManager));
431        assert!(components.contains(&ComponentType::ProtoProcessor));
432    }
433}